diff options
Diffstat (limited to 'src/util/perf/u_trace.py')
-rw-r--r-- | src/util/perf/u_trace.py | 526 |
1 files changed, 426 insertions, 100 deletions
diff --git a/src/util/perf/u_trace.py b/src/util/perf/u_trace.py index d0d99c828ee..34796a4b1a1 100644 --- a/src/util/perf/u_trace.py +++ b/src/util/perf/u_trace.py @@ -22,16 +22,20 @@ # from mako.template import Template +from mako import exceptions from collections import namedtuple -from enum import Flag, auto +from enum import IntEnum import os TRACEPOINTS = {} +TRACEPOINTS_TOGGLES = {} 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): + def __init__(self, name, args=[], toggle_name=None, + tp_struct=None, tp_print=None, tp_perfetto=None, + tp_markers=None, tp_flags=[], need_cs_param=True): """Parameters: - name: the tracepoint name, a tracepoint function with the given @@ -42,25 +46,76 @@ class Tracepoint(object): - tp_print: (optional) array of format string followed by expressions - tp_perfetto: (optional) driver provided callback which can generate perfetto events + - tp_markers: (optional) driver provided printf-style callback which can + generate CS markers, this requires 'need_cs_param' as the first param + is the CS that the label should be emitted into + - need_cs_param: whether tracepoint functions need an additional cs + parameter. """ assert isinstance(name, str) assert isinstance(args, list) assert name not in TRACEPOINTS + def needs_storage(a): + if a.c_format is None: + return False + if a.is_indirect: + return False + return True + self.name = name self.args = args - if tp_struct is None: - tp_struct = args - self.tp_struct = tp_struct - self.tp_print = tp_print + # For storage data, include all the specified tp_struct by the caller + # as well as arguments needing storage + self.tp_struct = [] + if tp_struct is not None: + self.tp_struct += tp_struct + self.tp_struct += [x for x in args if needs_storage(x)] + # For printing, include all the arguments & tp_struct elements that + # have a format printer + self.tp_print = [x for x in args if x.c_format is not None] + if tp_struct is not None: + self.tp_print += [x for x in tp_struct if x.c_format is not None] + + self.has_variable_arg = False + for arg in self.tp_struct: + if arg.length_arg != None and not arg.length_arg.isdigit(): + self.has_variable_arg = True + break + self.tp_print_custom = tp_print + + # Compute the offset of each indirect argument + self.indirect_args = [x for x in args if x.is_indirect] + indirect_sizes = [] + for indirect in self.indirect_args: + indirect.indirect_offset = ' + '.join(indirect_sizes) if len(indirect_sizes) > 0 else 0 + indirect_sizes.append(f"sizeof({indirect.type}") + self.tp_perfetto = tp_perfetto + self.tp_markers = tp_markers + self.tp_flags = tp_flags + self.toggle_name = toggle_name + self.need_cs_param = need_cs_param TRACEPOINTS[name] = self + if toggle_name is not None and toggle_name not in TRACEPOINTS_TOGGLES: + TRACEPOINTS_TOGGLES[toggle_name] = len(TRACEPOINTS_TOGGLES) + + def can_generate_print(self): + return self.args is not None and len(self.args) > 0 + + def enabled_expr(self, trace_toggle_name): + if trace_toggle_name is None: + return "true" + assert self.toggle_name is not None + return "({0} & {1}_{2})".format(trace_toggle_name, + trace_toggle_name.upper(), + self.toggle_name.upper()) class TracepointArgStruct(): """Represents struct that is being passed as an argument """ - def __init__(self, type, var): + def __init__(self, type, var, c_format=None, fields=[], is_indirect=False): """Parameters: - type: argument's C type. @@ -71,11 +126,36 @@ class TracepointArgStruct(): self.type = type self.var = var + self.name = var + self.is_indirect = is_indirect + self.indirect_offset = 0 + self.is_struct = True + self.c_format = c_format + self.fields = fields + self.to_prim_type = None + self.perfetto_field = None + + if self.is_indirect: + self.func_param = f"struct u_trace_address {self.var}" + else: + self.func_param = f"{self.type} {self.var}" + + def value_expr(self, entry_name): + ret = None + if self.is_struct: + if self.is_indirect: + ret = ", ".join([f"__{self.name}->{f}" for f in self.fields]) + else: + ret = ", ".join([f"{entry_name}->{self.name}.{f}" for f in self.fields]) + else: + ret = f"{entry_name}->{self.name}" + return ret 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): + def __init__(self, type, var, c_format=None, name=None, to_prim_type=None, + length_arg=None, copy_func=None, is_indirect=False, perfetto_field=None): """Parameters: - type: argument's C type. @@ -85,10 +165,12 @@ class TracepointArg(object): 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. + - length_arg: whether this argument is a variable length array + - perfetto_field: Whether the argument should be set to a perfetto field + with the given name, as opposed to attached with add_extra_data(). """ assert isinstance(type, str) assert isinstance(var, str) - assert isinstance(c_format, str) self.type = type self.var = var @@ -97,18 +179,52 @@ class TracepointArg(object): name = var self.name = name self.to_prim_type = to_prim_type + self.length_arg = length_arg + self.copy_func = copy_func + self.perfetto_field = perfetto_field + + self.is_struct = False + self.is_indirect = is_indirect + self.indirect_offset = 0 + + if self.is_indirect: + pass + elif self.type == "str": + if self.length_arg and self.length_arg.isdigit(): + self.struct_member = f"char {self.name}[{length_arg} + 1]" + else: + self.struct_member = f"char {self.name}[0]" + else: + self.struct_member = f"{self.type} {self.name}" + + if self.is_indirect: + self.func_param = f"struct u_trace_address {self.var}" + elif self.type == "str": + self.func_param = f"const char *{self.var}" + else: + self.func_param = f"{self.type} {self.var}" + + def value_expr(self, entry_name): + if self.is_indirect: + ret = f"*__{self.name}" + else: + ret = f"{entry_name}->{self.name}" + if not self.is_struct and self.to_prim_type: + ret = self.to_prim_type.format(ret) + return ret HEADERS = [] -class HeaderScope(Flag): - HEADER = auto() - SOURCE = auto() +class HeaderScope(IntEnum): + HEADER = (1 << 0) + SOURCE = (1 << 1) + PERFETTO = (1 << 2) class Header(object): """Class that represents a header file dependency of generated tracepoints """ - def __init__(self, hdr, scope=HeaderScope.HEADER|HeaderScope.SOURCE): + def __init__(self, hdr, scope=HeaderScope.HEADER): """Parameters: - hdr: the required header path @@ -173,50 +289,81 @@ extern "C" { ${declaration.decl}; % endfor +% if trace_toggle_name is not None: +enum ${trace_toggle_name.lower()} { +% for toggle_name, config_id in TRACEPOINTS_TOGGLES.items(): + ${trace_toggle_name.upper()}_${toggle_name.upper()} = 1ull << ${config_id}, +% endfor +}; + +extern uint64_t ${trace_toggle_name}; + +void ${trace_toggle_name}_config_variable(void); +% endif + % for trace_name, trace in TRACEPOINTS.items(): + /* * ${trace_name} */ struct trace_${trace_name} { % for arg in trace.tp_struct: - ${arg.type} ${arg.name}; + ${arg.struct_member}; % 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; +% if len(trace.tp_struct) == 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); +void ${trace.tp_perfetto}( + ${ctx_param}, + uint64_t ts_ns, + uint16_t tp_idx, + const void *flush_data, + const struct trace_${trace_name} *payload, + const void *indirect_data); #endif % endif -void __trace_${trace_name}(struct u_trace *ut, void *cs +void __trace_${trace_name}( + struct u_trace *ut + , enum u_trace_type enabled_traces +% if trace.need_cs_param: + , void *cs +% endif % for arg in trace.args: - , ${arg.type} ${arg.var} + , ${arg.func_param} % endfor ); -static inline void trace_${trace_name}(struct u_trace *ut, void *cs +static ALWAYS_INLINE void trace_${trace_name}( + struct u_trace *ut +% if trace.need_cs_param: + , void *cs +% endif % for arg in trace.args: - , ${arg.type} ${arg.var} + , ${arg.func_param} % endfor ) { -% if trace.tp_perfetto is not None: - if (!unlikely(ut->enabled || ut_perfetto_enabled)) -% else: - if (!unlikely(ut->enabled)) -% endif + enum u_trace_type enabled_traces = p_atomic_read_relaxed(&ut->utctx->enabled_traces); + if (!unlikely(enabled_traces != 0 && + ${trace.enabled_expr(trace_toggle_name)})) return; - __trace_${trace_name}(ut, cs + __trace_${trace_name}( + ut + , enabled_traces +% if trace.need_cs_param: + , cs +% endif % for arg in trace.args: - , ${arg.var} + , ${arg.var} % endfor ); } @@ -252,92 +399,248 @@ src_template = """\ * IN THE SOFTWARE. */ +#include "${hdr}" + % for header in HEADERS: #include "${header.hdr}" % endfor -#include "${hdr}" - #define __NEEDS_TRACE_PRIV +#include "util/u_debug.h" #include "util/perf/u_trace_priv.h" -% for trace_name, trace in TRACEPOINTS.items(): +% if trace_toggle_name is not None: +static const struct debug_control config_control[] = { +% for toggle_name in TRACEPOINTS_TOGGLES.keys(): + { "${toggle_name}", ${trace_toggle_name.upper()}_${toggle_name.upper()}, }, +% endfor + { NULL, 0, }, +}; +uint64_t ${trace_toggle_name} = 0; + +static void +${trace_toggle_name}_variable_once(void) +{ + uint64_t default_value = 0 +% for name in trace_toggle_defaults: + | ${trace_toggle_name.upper()}_${name.upper()} +% endfor + ; + + ${trace_toggle_name} = + parse_enable_string(getenv("${trace_toggle_name.upper()}"), + default_value, + config_control); +} + +void +${trace_toggle_name}_config_variable(void) +{ + static once_flag process_${trace_toggle_name}_variable_flag = ONCE_FLAG_INIT; + + call_once(&process_${trace_toggle_name}_variable_flag, + ${trace_toggle_name}_variable_once); +} +% endif + +% for index, (trace_name, trace) in enumerate(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) { + % if trace.can_generate_print(): +static void __print_${trace_name}(FILE *out, const void *arg, const void *indirect) { + % if len(trace.tp_struct) > 0: 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:]: + % endif + % for arg in trace.indirect_args: + const ${arg.type} *__${arg.name} = (const ${arg.type} *) ((char *)indirect + ${arg.indirect_offset}); + % endfor + % if trace.tp_print_custom is not None: + fprintf(out, "${trace.tp_print_custom[0]}\\n" + % for arg in trace.tp_print_custom[1:]: , ${arg} -% endfor -% else: + % endfor + % else: fprintf(out, "" -% for arg in trace.tp_struct: + % for arg in trace.tp_print: "${arg.name}=${arg.c_format}, " -% endfor + % 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 + % for arg in trace.tp_print: + ,${arg.value_expr("__entry")} + % endfor + % endif + ); +} + +static void __print_json_${trace_name}(FILE *out, const void *arg, const void *indirect) { + % if len(trace.tp_struct) > 0: + const struct trace_${trace_name} *__entry = + (const struct trace_${trace_name} *)arg; + % endif + % for arg in trace.indirect_args: + const ${arg.type} *__${arg.var} = (const ${arg.type} *) ((char *)indirect + ${arg.indirect_offset}); + % endfor + % if trace.tp_print_custom is not None: + fprintf(out, "\\"unstructured\\": \\"${trace.tp_print_custom[0]}\\"" + % for arg in trace.tp_print_custom[1:]: + , ${arg} + % endfor + % else: + fprintf(out, "" + % for arg in trace.tp_print: + "\\"${arg.name}\\": \\"${arg.c_format}\\"" + % if arg != trace.tp_print[-1]: + ", " + % endif + % endfor + % for arg in trace.tp_print: + ,${arg.value_expr("__entry")} + % endfor + % endif ); } -% else: + + % else: #define __print_${trace_name} NULL -% endif +#define __print_json_${trace_name} NULL + % endif + % if trace.tp_markers is not None: + +__attribute__((format(printf, 3, 4))) void ${trace.tp_markers}(struct u_trace_context *utctx, void *, const char *, ...); + +static void __emit_label_${trace_name}(struct u_trace_context *utctx, void *cs, struct trace_${trace_name} *entry) { + ${trace.tp_markers}(utctx, cs, "${trace_name}(" + % for idx,arg in enumerate(trace.tp_print): + % if not arg.is_indirect: + "${"," if idx != 0 else ""}${arg.name}=${arg.c_format}" + % endif + % endfor + ")" + % for arg in trace.tp_print: + % if not arg.is_indirect: + ,${arg.value_expr('entry')} + % endif + % endfor + ); +} + + % endif static const struct u_tracepoint __tp_${trace_name} = { - ALIGN_POT(sizeof(struct trace_${trace_name}), 8), /* keep size 64b aligned */ "${trace_name}", + ALIGN_POT(sizeof(struct trace_${trace_name}), 8), /* keep size 64b aligned */ + 0 + % for arg in trace.indirect_args: + + sizeof(${arg.type}) + % endfor + , + ${0 if len(trace.tp_flags) == 0 else " | ".join(trace.tp_flags)}, + ${index}, __print_${trace_name}, -% if trace.tp_perfetto is not None: + __print_json_${trace_name}, + % if trace.tp_perfetto is not None: #ifdef HAVE_PERFETTO - (void (*)(void *pctx, uint64_t, const void *, const void *))${trace.tp_perfetto}, + (void (*)(void *pctx, uint64_t, uint16_t, const void *, const void *, const void *))${trace.tp_perfetto}, #endif -% endif + % endif }; -void __trace_${trace_name}(struct u_trace *ut, void *cs -% for arg in trace.args: - , ${arg.type} ${arg.var} -% endfor +void __trace_${trace_name}( + struct u_trace *ut + , enum u_trace_type enabled_traces + % if trace.need_cs_param: + , void *cs + % endif + % for arg in trace.args: + , ${arg.func_param} + % 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 + struct trace_${trace_name} entry; + % if len(trace.indirect_args) > 0: + struct u_trace_address indirects[] = { + % for arg in trace.indirect_args: + ${arg.var}, + % endfor + }; + uint8_t indirect_sizes[] = { + % for arg in trace.indirect_args: + sizeof(${arg.type}), + % endfor + }; + % endif + UNUSED struct trace_${trace_name} *__entry = + enabled_traces & U_TRACE_TYPE_REQUIRE_QUEUING ? + (struct trace_${trace_name} *)u_trace_appendv(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name}, + 0 + % for arg in trace.tp_struct: + % if arg.length_arg is not None and not arg.length_arg.isdigit(): + + ${arg.length_arg} + % endif + % endfor + , + % if len(trace.indirect_args) > 0: + ARRAY_SIZE(indirects), indirects, indirect_sizes + % else: + 0, NULL, NULL + % endif + ) : + &entry; + % for arg in trace.tp_struct: + % if arg.copy_func is None: + __entry->${arg.name} = ${arg.var}; + % else: + ${arg.copy_func}(__entry->${arg.name}, ${arg.var}, ${arg.length_arg}); + % endif + % endfor + % if trace.tp_markers is not None: + if (enabled_traces & U_TRACE_TYPE_MARKERS) + __emit_label_${trace_name}(ut->utctx, cs, __entry); + % endif } % endfor """ -def utrace_generate(cpath, hpath, ctx_param): +def utrace_generate(cpath, hpath, ctx_param, trace_toggle_name=None, + trace_toggle_defaults=[]): + """Parameters: + + - cpath: c file to generate. + - hpath: h file to generate. + - ctx_param: type of the first parameter to the perfetto vfuncs. + - trace_toggle_name: (optional) name of the environment variable + enabling/disabling tracepoints. + - trace_toggle_defaults: (optional) list of tracepoints enabled by default. + """ 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)) + with open(cpath, 'w', encoding='utf-8') as f: + try: + f.write(Template(src_template).render( + hdr=hdr, + ctx_param=ctx_param, + trace_toggle_name=trace_toggle_name, + trace_toggle_defaults=trace_toggle_defaults, + HEADERS=[h for h in HEADERS if h.scope & HeaderScope.SOURCE], + TRACEPOINTS=TRACEPOINTS, + TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES)) + except: + print(exceptions.text_error_template().render()) 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)) + with open(hpath, 'w', encoding='utf-8') as f: + try: + f.write(Template(hdr_template).render( + hdrname=hdr.rstrip('.h').upper(), + ctx_param=ctx_param, + trace_toggle_name=trace_toggle_name, + HEADERS=[h for h in HEADERS if h.scope & HeaderScope.HEADER], + FORWARD_DECLS=FORWARD_DECLS, + TRACEPOINTS=TRACEPOINTS, + TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES)) + except: + print(exceptions.text_error_template().render()) perfetto_utils_hdr_template = """\ @@ -370,40 +673,63 @@ perfetto_utils_hdr_template = """\ #include <perfetto.h> +% for header in HEADERS: +#include "${header.hdr}" +% endfor + +UNUSED static const char *${basename}_names[] = { +% for trace_name, trace in TRACEPOINTS.items(): + "${trace_name}", +% endfor +}; + +typedef void (*trace_payload_as_extra_func)(perfetto::protos::pbzero::GpuRenderStageEvent *, const void*, const void*); + % 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) + const struct trace_${trace_name} *payload, + const void *indirect_data) { -% if all([trace.tp_perfetto, trace.tp_struct]) and len(trace.tp_struct) > 0: + % if trace.tp_perfetto is not None and len(trace.tp_print) > 0: + % if any(not arg.perfetto_field for arg in trace.tp_print): char buf[128]; + % endif -% for arg in trace.tp_struct: + % for arg in trace.tp_print: + % if arg.perfetto_field: + event->set_${arg.name}(${arg.value_expr("payload")}); + % else: { 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 + % if arg.is_indirect: + const ${arg.type}* __${arg.var} = (const ${arg.type}*)((uint8_t *)indirect_data + ${arg.indirect_offset}); + % endif + sprintf(buf, "${arg.c_format}", ${arg.value_expr("payload")}); data->set_value(buf); } -% endfor + % endif + % endfor -% endif + % endif } % endfor #endif /* ${guard_name} */ """ -def utrace_generate_perfetto_utils(hpath): +def utrace_generate_perfetto_utils(hpath,basename="tracepoint"): 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)) + with open(hpath, 'w', encoding='utf-8') as f: + try: + f.write(Template(perfetto_utils_hdr_template).render( + basename=basename, + hdrname=hdr.rstrip('.h').upper(), + HEADERS=[h for h in HEADERS if h.scope & HeaderScope.PERFETTO], + TRACEPOINTS=TRACEPOINTS)) + except: + print(exceptions.text_error_template().render()) |