diff options
author | Danylo Piliaiev <dpiliaiev@igalia.com> | 2021-05-25 18:16:07 +0300 |
---|---|---|
committer | Danylo Piliaiev <dpiliaiev@igalia.com> | 2021-09-10 14:58:28 +0300 |
commit | cefaa73909718e570b36c5f8463e4b534e96201a (patch) | |
tree | 2636bfa02f52eec2862fcbe34ce7a942274545ab /src | |
parent | e14f525280ee9f09988239adbead1c9dff5476f0 (diff) |
util/u_trace: auto-generation of serialization funcs for tracepoints
Add ability to auto-generate:
- printing of args for "GPU_TRACE=1", still could be overriden with
tp_print.
- population of extra data for perfetto event.
Signed-off-by: Danylo Piliaiev <dpiliaiev@igalia.com>
Reviewed-by: Rob Clark <robdclark@chromium.org>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/10969>
Diffstat (limited to 'src')
-rw-r--r-- | src/gallium/auxiliary/util/u_trace_gallium.c | 12 | ||||
-rw-r--r-- | src/gallium/auxiliary/util/u_tracepoints.py | 40 | ||||
-rw-r--r-- | src/gallium/drivers/freedreno/freedreno_tracepoints.py | 51 | ||||
-rw-r--r-- | src/util/perf/u_trace.py | 168 |
4 files changed, 201 insertions, 70 deletions
diff --git a/src/gallium/auxiliary/util/u_trace_gallium.c b/src/gallium/auxiliary/util/u_trace_gallium.c index dbc7f4d30cb..e8fd0f809d2 100644 --- a/src/gallium/auxiliary/util/u_trace_gallium.c +++ b/src/gallium/auxiliary/util/u_trace_gallium.c @@ -27,6 +27,8 @@ #include "pipe/p_context.h" #include "pipe/p_screen.h" +#include "u_tracepoints.h" + #ifdef __cplusplus extern "C" { #endif @@ -71,23 +73,21 @@ u_trace_pipe_context_init(struct u_trace_context *utctx, delete_flush_data); } -void __trace_surface(struct u_trace *ut, const struct pipe_surface *psurf); -void __trace_framebuffer(struct u_trace *ut, const struct pipe_framebuffer_state *pfb); - inline void trace_framebuffer_state(struct u_trace *ut, const struct pipe_framebuffer_state *pfb) { if (likely(!ut->enabled)) return; - __trace_framebuffer(ut, pfb); + trace_framebuffer(ut, pfb); + for (unsigned i = 0; i < pfb->nr_cbufs; i++) { if (pfb->cbufs[i]) { - __trace_surface(ut, pfb->cbufs[i]); + trace_surface(ut, pfb->cbufs[i]); } } if (pfb->zsbuf) { - __trace_surface(ut, pfb->zsbuf); + trace_surface(ut, pfb->zsbuf); } } diff --git a/src/gallium/auxiliary/util/u_tracepoints.py b/src/gallium/auxiliary/util/u_tracepoints.py index a9b83215c39..30aaab9df1d 100644 --- a/src/gallium/auxiliary/util/u_tracepoints.py +++ b/src/gallium/auxiliary/util/u_tracepoints.py @@ -37,6 +37,8 @@ sys.path.insert(0, args.import_path) from u_trace import Header from u_trace import Tracepoint +from u_trace import TracepointArg as Arg +from u_trace import TracepointArgStruct as ArgStruct from u_trace import utrace_generate # @@ -47,11 +49,11 @@ Header('pipe/p_state.h') Header('util/format/u_format.h') Tracepoint('surface', - args=[['const struct pipe_surface *', 'psurf']], - tp_struct=[['uint16_t', 'width', 'psurf->width'], - ['uint16_t', 'height', 'psurf->height'], - ['uint8_t', 'nr_samples', 'psurf->nr_samples'], - ['const char *', 'format', 'util_format_short_name(psurf->format)']], + args=[ArgStruct(type='const struct pipe_surface *', var='psurf')], + tp_struct=[Arg(type='uint16_t', name='width', var='psurf->width', c_format='%u'), + Arg(type='uint16_t', name='height', var='psurf->height', c_format='%u'), + Arg(type='uint8_t', name='nr_samples', var='psurf->nr_samples', c_format='%u'), + Arg(type='const char *', name='format', var='util_format_short_name(psurf->format)', c_format='%s')], tp_print=['%ux%u@%u, fmt=%s', '__entry->width', '__entry->height', @@ -61,12 +63,12 @@ Tracepoint('surface', # Note: called internally from trace_framebuffer_state() Tracepoint('framebuffer', - args=[['const struct pipe_framebuffer_state *', 'pfb']], - tp_struct=[['uint16_t', 'width', 'pfb->width'], - ['uint16_t', 'height', 'pfb->height'], - ['uint8_t', 'layers', 'pfb->layers'], - ['uint8_t', 'samples', 'pfb->samples'], - ['uint8_t', 'nr_cbufs', 'pfb->nr_cbufs']], + args=[ArgStruct(type='const struct pipe_framebuffer_state *', var='pfb')], + tp_struct=[Arg(type='uint16_t', name='width', var='pfb->width', c_format='%u'), + Arg(type='uint16_t', name='height', var='pfb->height', c_format='%u'), + Arg(type='uint8_t', name='layers', var='pfb->layers', c_format='%u'), + Arg(type='uint8_t', name='samples', var='pfb->samples', c_format='%u'), + Arg(type='uint8_t', name='nr_cbufs', var='pfb->nr_cbufs', c_format='%u')], tp_print=['%ux%ux%u@%u, nr_cbufs: %u', '__entry->width', '__entry->height', @@ -76,14 +78,14 @@ Tracepoint('framebuffer', ) Tracepoint('grid_info', - args=[['const struct pipe_grid_info *', 'pgrid']], - tp_struct=[['uint8_t', 'work_dim', 'pgrid->work_dim'], - ['uint16_t', 'block_x', 'pgrid->block[0]'], - ['uint16_t', 'block_y', 'pgrid->block[1]'], - ['uint16_t', 'block_z', 'pgrid->block[2]'], - ['uint16_t', 'grid_x', 'pgrid->grid[0]'], - ['uint16_t', 'grid_y', 'pgrid->grid[1]'], - ['uint16_t', 'grid_z', 'pgrid->grid[2]']], + args=[ArgStruct(type='const struct pipe_grid_info *', var='pgrid')], + tp_struct=[Arg(type='uint8_t', name='work_dim', var='pgrid->work_dim', c_format='%u'), + Arg(type='uint16_t', name='block_x', var='pgrid->block[0]', c_format='%u'), + Arg(type='uint16_t', name='block_y', var='pgrid->block[1]', c_format='%u'), + Arg(type='uint16_t', name='block_z', var='pgrid->block[2]', c_format='%u'), + Arg(type='uint16_t', name='grid_x', var='pgrid->grid[0]', c_format='%u'), + Arg(type='uint16_t', name='grid_y', var='pgrid->grid[1]', c_format='%u'), + Arg(type='uint16_t', name='grid_z', var='pgrid->grid[2]', c_format='%u')], tp_print=['work_dim=%u, block=%ux%ux%u, grid=%ux%ux%u', '__entry->work_dim', '__entry->block_x', '__entry->block_y', '__entry->block_z', '__entry->grid_x', '__entry->grid_y', '__entry->grid_z'], diff --git a/src/gallium/drivers/freedreno/freedreno_tracepoints.py b/src/gallium/drivers/freedreno/freedreno_tracepoints.py index b0a5a016c6b..095009b6b80 100644 --- a/src/gallium/drivers/freedreno/freedreno_tracepoints.py +++ b/src/gallium/drivers/freedreno/freedreno_tracepoints.py @@ -37,6 +37,7 @@ sys.path.insert(0, args.import_path) from u_trace import Header from u_trace import Tracepoint +from u_trace import TracepointArg from u_trace import utrace_generate # @@ -50,19 +51,19 @@ Tracepoint('start_state_restore') Tracepoint('end_state_restore') Tracepoint('flush_batch', - args=[['struct fd_batch *', 'batch'], - ['uint16_t', 'cleared'], - ['uint16_t', 'gmem_reason'], - ['uint16_t', 'num_draws']], + args=[TracepointArg(type='struct fd_batch *', var='batch', c_format='%x'), + TracepointArg(type='uint16_t', var='cleared', c_format='%x'), + TracepointArg(type='uint16_t', var='gmem_reason', c_format='%x'), + TracepointArg(type='uint16_t', var='num_draws', c_format='%u')], tp_print=['%p: cleared=%x, gmem_reason=%x, num_draws=%u', '__entry->batch', '__entry->cleared', '__entry->gmem_reason', '__entry->num_draws'], ) Tracepoint('render_gmem', - args=[['uint16_t', 'nbins_x'], - ['uint16_t', 'nbins_y'], - ['uint16_t', 'bin_w'], - ['uint16_t', 'bin_h']], + args=[TracepointArg(type='uint16_t', var='nbins_x', c_format='%u'), + TracepointArg(type='uint16_t', var='nbins_y', c_format='%u'), + TracepointArg(type='uint16_t', var='bin_w', c_format='%u'), + TracepointArg(type='uint16_t', var='bin_h', c_format='%u')], tp_print=['%ux%u bins of %ux%u', '__entry->nbins_x', '__entry->nbins_y', '__entry->bin_w', '__entry->bin_h'], ) @@ -72,16 +73,16 @@ Tracepoint('render_sysmem') # Note that this doesn't include full information about all of the MRTs # but seems to roughly match what I see with a blob trace Tracepoint('start_render_pass', - args=[['uint32_t', 'submit_id'], - ['enum pipe_format', 'cbuf0_format'], - ['enum pipe_format', 'zs_format'], - ['uint16_t', 'width'], - ['uint16_t', 'height'], - ['uint8_t', 'mrts'], - ['uint8_t', 'samples'], - ['uint16_t', 'nbins'], - ['uint16_t', 'binw'], - ['uint16_t', 'binh']], + args=[TracepointArg(type='uint32_t', var='submit_id', c_format='%u'), + TracepointArg(type='enum pipe_format', var='cbuf0_format', c_format='%s', to_prim_type='util_format_description({})->short_name'), + TracepointArg(type='enum pipe_format', var='zs_format', c_format='%s', to_prim_type='util_format_description({})->short_name'), + TracepointArg(type='uint16_t', var='width', c_format='%u'), + TracepointArg(type='uint16_t', var='height', c_format='%u'), + TracepointArg(type='uint8_t', var='mrts', c_format='%u'), + TracepointArg(type='uint8_t', var='samples', c_format='%u'), + TracepointArg(type='uint16_t', var='nbins', c_format='%u'), + TracepointArg(type='uint16_t', var='binw', c_format='%u'), + TracepointArg(type='uint16_t', var='binh', c_format='%u')], tp_perfetto='fd_start_render_pass' ) Tracepoint('end_render_pass', @@ -100,7 +101,7 @@ Tracepoint('end_prologue') # For GMEM pass, where this could either be a clear or resolve Tracepoint('start_clear_restore', - args=[['uint16_t', 'fast_cleared']], + args=[TracepointArg(type='uint16_t', var='fast_cleared', c_format='0x%x')], tp_print=['fast_cleared: 0x%x', '__entry->fast_cleared'], tp_perfetto='fd_start_clear_restore', ) @@ -113,10 +114,10 @@ Tracepoint('end_resolve', tp_perfetto='fd_end_resolve') Tracepoint('start_tile', - args=[['uint16_t', 'bin_h'], - ['uint16_t', 'yoff'], - ['uint16_t', 'bin_w'], - ['uint16_t', 'xoff']], + args=[TracepointArg(type='uint16_t', var='bin_h', c_format='%u'), + TracepointArg(type='uint16_t', var='yoff', c_format='%u'), + TracepointArg(type='uint16_t', var='bin_w', c_format='%u'), + TracepointArg(type='uint16_t', var='xoff', c_format='%u')], tp_print=['bin_h=%d, yoff=%d, bin_w=%d, xoff=%d', '__entry->bin_h', '__entry->yoff', '__entry->bin_w', '__entry->xoff'], ) @@ -127,8 +128,8 @@ Tracepoint('end_draw_ib', tp_perfetto='fd_end_draw_ib') Tracepoint('start_blit', - args=[['enum pipe_texture_target', 'src_target'], - ['enum pipe_texture_target', 'dst_target']], + args=[TracepointArg(type='enum pipe_texture_target', var='src_target', c_format='%s', to_prim_type="util_str_tex_target({}, true)"), + TracepointArg(type='enum pipe_texture_target', var='dst_target', c_format='%s', to_prim_type="util_str_tex_target({}, true)")], tp_print=['%s -> %s', 'util_str_tex_target(__entry->src_target, true)', 'util_str_tex_target(__entry->dst_target, true)'], tp_perfetto='fd_start_blit', diff --git a/src/util/perf/u_trace.py b/src/util/perf/u_trace.py index e6b74d498e0..428076a1962 100644 --- a/src/util/perf/u_trace.py +++ b/src/util/perf/u_trace.py @@ -22,6 +22,8 @@ # from mako.template import Template +from collections import namedtuple +from enum import Flag, auto import os TRACEPOINTS = {} @@ -36,10 +38,7 @@ class Tracepoint(object): 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 [type, name] pairs - - tp_struct: (optional) array of [type, name, expr] tuples to - convert from tracepoint args to trace payload. If not specified - it will be generated from `args` (ie, [type, name, name]) + - 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 @@ -51,27 +50,72 @@ class Tracepoint(object): self.name = name self.args = args if tp_struct is None: - tp_struct = [] - for arg in args: - tp_struct.append([arg[0], arg[1], arg[1]]) + 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(Flag): + HEADER = auto() + SOURCE = auto() + class Header(object): """Class that represents a header file dependency of generated tracepoints """ - def __init__(self, hdr): + 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) @@ -134,10 +178,10 @@ ${declaration.decl}; * ${trace_name} */ struct trace_${trace_name} { -% for member in trace.tp_struct: - ${member[0]} ${member[1]}; +% for arg in trace.tp_struct: + ${arg.type} ${arg.name}; % endfor -% if len(trace.tp_struct) == 0: +% 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 @@ -156,12 +200,12 @@ void ${trace.tp_perfetto}(${ctx_param}, uint64_t ts_ns, const void *flush_data, % endif void __trace_${trace_name}(struct u_trace *ut % for arg in trace.args: - , ${arg[0]} ${arg[1]} + , ${arg.type} ${arg.var} % endfor ); static inline void trace_${trace_name}(struct u_trace *ut % for arg in trace.args: - , ${arg[0]} ${arg[1]} + , ${arg.type} ${arg.var} % endfor ) { % if trace.tp_perfetto is not None: @@ -172,7 +216,7 @@ static inline void trace_${trace_name}(struct u_trace *ut return; __trace_${trace_name}(ut % for arg in trace.args: - , ${arg[1]} + , ${arg.var} % endfor ); } @@ -221,14 +265,29 @@ src_template = """\ /* * ${trace_name} */ -% if trace.tp_print is not None: +% 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: @@ -246,14 +305,14 @@ static const struct u_tracepoint __tp_${trace_name} = { }; void __trace_${trace_name}(struct u_trace *ut % for arg in trace.args: - , ${arg[0]} ${arg[1]} + , ${arg.type} ${arg.var} % endfor ) { struct trace_${trace_name} *__entry = (struct trace_${trace_name} *)u_trace_append(ut, &__tp_${trace_name}); (void)__entry; -% for member in trace.tp_struct: - __entry->${member[1]} = ${member[2]}; +% for arg in trace.tp_struct: + __entry->${arg.name} = ${arg.var}; % endfor } @@ -267,7 +326,7 @@ def utrace_generate(cpath, hpath, ctx_param): f.write(Template(src_template).render( hdr=hdr, ctx_param=ctx_param, - HEADERS=HEADERS, + HEADERS=[h for h in HEADERS if h.scope & HeaderScope.SOURCE], TRACEPOINTS=TRACEPOINTS)) if hpath is not None: @@ -276,6 +335,75 @@ def utrace_generate(cpath, hpath, ctx_param): f.write(Template(hdr_template).render( hdrname=hdr.rstrip('.h').upper(), ctx_param=ctx_param, - HEADERS=HEADERS, + 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)) |