mesa/src/util/perf/u_trace.py
Danylo Piliaiev 4092d5f0d8 u_trace: pass command stream through tracing functions
Allows writing timestamps into different command streams.

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>
2021-09-10 14:58:28 +03:00

409 lines
13 KiB
Python

#
# 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 Flag, auto
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(Flag):
HEADER = auto()
SOURCE = auto()
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))