gallium/aux: Add GPU tracepoint mechanism

This adds a mechanism, loosely inspired by the linux kernel's tracepoint
mechanism, to declare and emit tracepoints.  A driver provided callback
is used to emit cmdstream to capture timestamps on the GPU, which are
used to later "render" the emitted tracepoints.

Signed-off-by: Rob Clark <robdclark@chromium.org>
Acked-by: Antonio Caggiano <antonio.caggiano@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7818>
This commit is contained in:
Rob Clark 2020-11-26 11:14:22 -08:00 committed by Marge Bot
parent a1440ec3da
commit 3471af9c6c
6 changed files with 943 additions and 0 deletions

View File

@ -314,6 +314,9 @@ files_libgallium = files(
'util/u_texture.h',
'util/u_tile.c',
'util/u_tile.h',
'util/u_trace.c',
'util/u_trace.h',
'util/u_trace_priv.h',
'util/u_transfer.c',
'util/u_transfer.h',
'util/u_transfer_helper.c',
@ -480,6 +483,25 @@ if with_dri2 and with_platform_x11
endif
endif
u_trace_py = files('util/u_trace.py')
files_u_tracepoints = custom_target(
'u_tracepoints.[ch]',
input: 'util/u_tracepoints.py',
output: ['u_tracepoints.c', 'u_tracepoints.h'],
command: [
prog_python, '@INPUT@',
'-p', join_paths(meson.source_root(), 'src/gallium/auxiliary/util/'),
'-C', '@OUTPUT0@',
'-H', '@OUTPUT1@',
],
depend_files: u_trace_py,
)
files_libgallium += files_u_tracepoints
idep_u_tracepoints = declare_dependency(
sources: files_u_tracepoints,
)
u_indices_gen_c = custom_target(
'u_indices_gen.c',
input : 'indices/u_indices_gen.py',

View File

@ -0,0 +1,348 @@
/*
* Copyright © 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.
*/
#include <inttypes.h>
#include "pipe/p_context.h"
#include "pipe/p_state.h"
#include "util/list.h"
#include "util/ralloc.h"
#include "util/u_debug.h"
#include "util/u_inlines.h"
#include "u_fifo.h"
#include "u_trace.h"
#define __NEEDS_TRACE_PRIV
#include "u_trace_priv.h"
#define TIMESTAMP_BUF_SIZE 0x1000
#define TRACES_PER_CHUNK (TIMESTAMP_BUF_SIZE / sizeof(uint64_t))
struct u_trace_event {
const struct u_tracepoint *tp;
const void *payload;
};
/**
* A "chunk" of trace-events and corresponding timestamp buffer. As
* trace events are emitted, additional trace chucks will be allocated
* as needed. When u_trace_flush() is called, they are transferred
* from the u_trace to the u_trace_context queue.
*/
struct u_trace_chunk {
struct list_head node;
struct u_trace_context *utctx;
/* The number of traces this chunk contains so far: */
unsigned num_traces;
/* table of trace events: */
struct u_trace_event traces[TRACES_PER_CHUNK];
/* table of driver recorded 64b timestamps, index matches index
* into traces table
*/
struct pipe_resource *timestamps;
/**
* For trace payload, we sub-allocate from ralloc'd buffers which
* hang off of the chunk's ralloc context, so they are automatically
* free'd when the chunk is free'd
*/
uint8_t *payload_buf, *payload_end;
struct util_queue_fence fence;
bool last; /* this chunk is last in batch */
bool eof; /* this chunk is last in frame */
};
static void
free_chunk(void *ptr)
{
struct u_trace_chunk *chunk = ptr;
pipe_resource_reference(&chunk->timestamps, NULL);
list_del(&chunk->node);
}
static void
free_chunks(struct list_head *chunks)
{
while (!list_is_empty(chunks)) {
struct u_trace_chunk *chunk = list_first_entry(chunks,
struct u_trace_chunk, node);
ralloc_free(chunk);
}
}
static struct u_trace_chunk *
get_chunk(struct u_trace *ut)
{
struct u_trace_chunk *chunk;
/* do we currently have a non-full chunk to append msgs to? */
if (!list_is_empty(&ut->trace_chunks)) {
chunk = list_last_entry(&ut->trace_chunks,
struct u_trace_chunk, node);
if (chunk->num_traces < TRACES_PER_CHUNK)
return chunk;
/* we need to expand to add another chunk to the batch, so
* the current one is no longer the last one of the batch:
*/
chunk->last = false;
}
/* .. if not, then create a new one: */
chunk = rzalloc_size(NULL, sizeof(*chunk));
ralloc_set_destructor(chunk, free_chunk);
chunk->utctx = ut->utctx;
struct pipe_resource tmpl = {
.target = PIPE_BUFFER,
.format = PIPE_FORMAT_R8_UNORM,
.bind = PIPE_BIND_QUERY_BUFFER | PIPE_BIND_LINEAR,
.width0 = TIMESTAMP_BUF_SIZE,
.height0 = 1,
.depth0 = 1,
.array_size = 1,
};
struct pipe_screen *pscreen = ut->utctx->pctx->screen;
chunk->timestamps = pscreen->resource_create(pscreen, &tmpl);
chunk->last = true;
list_addtail(&chunk->node, &ut->trace_chunks);
return chunk;
}
DEBUG_GET_ONCE_BOOL_OPTION(trace, "GALLIUM_GPU_TRACE", false)
DEBUG_GET_ONCE_FILE_OPTION(trace_file, "GALLIUM_GPU_TRACEFILE", NULL, "w")
static FILE *
get_tracefile(void)
{
static FILE *tracefile = NULL;
static bool firsttime = true;
if (firsttime) {
tracefile = debug_get_option_trace_file();
if (!tracefile && debug_get_option_trace()) {
tracefile = stdout;
}
firsttime = false;
}
return tracefile;
}
void
u_trace_context_init(struct u_trace_context *utctx,
struct pipe_context *pctx,
u_trace_record_ts record_timestamp,
u_trace_read_ts read_timestamp)
{
utctx->pctx = pctx;
utctx->record_timestamp = record_timestamp;
utctx->read_timestamp = read_timestamp;
utctx->last_time_ns = 0;
utctx->first_time_ns = 0;
utctx->frame_nr = 0;
list_inithead(&utctx->flushed_trace_chunks);
bool ret = util_queue_init(&utctx->queue, "traceq", 256, 1,
UTIL_QUEUE_INIT_USE_MINIMUM_PRIORITY |
UTIL_QUEUE_INIT_RESIZE_IF_FULL);
assert(ret);
utctx->out = ret ? get_tracefile() : NULL;
}
void
u_trace_context_fini(struct u_trace_context *utctx)
{
util_queue_finish(&utctx->queue);
util_queue_destroy(&utctx->queue);
if (utctx->out)
fflush(utctx->out);
free_chunks(&utctx->flushed_trace_chunks);
}
static void
process_chunk(void *job, int thread_index)
{
struct u_trace_chunk *chunk = job;
struct u_trace_context *utctx = chunk->utctx;
/* For first chunk of batch, accumulated times will be zerod: */
if (!utctx->last_time_ns) {
fprintf(utctx->out, "+----- NS -----+ +-- Δ --+ +----- MSG -----\n");
}
for (unsigned idx = 0; idx < chunk->num_traces; idx++) {
const struct u_trace_event *evt = &chunk->traces[idx];
uint64_t ns = utctx->read_timestamp(utctx, chunk->timestamps, idx);
int32_t delta;
if (!utctx->first_time_ns)
utctx->first_time_ns = ns;
if (ns != U_TRACE_NO_TIMESTAMP) {
delta = utctx->last_time_ns ? ns - utctx->last_time_ns : 0;
utctx->last_time_ns = ns;
} else {
/* we skipped recording the timestamp, so it should be
* the same as last msg:
*/
ns = utctx->last_time_ns;
delta = 0;
}
if (evt->tp->print) {
fprintf(utctx->out, "%016"PRIu64" %+9d: %s: ", ns, delta, evt->tp->name);
evt->tp->print(utctx->out, evt->payload);
} else {
fprintf(utctx->out, "%016"PRIu64" %+9d: %s\n", ns, delta, evt->tp->name);
}
}
if (chunk->last) {
uint64_t elapsed = utctx->last_time_ns - utctx->first_time_ns;
fprintf(utctx->out, "ELAPSED: %"PRIu64" ns\n", elapsed);
utctx->last_time_ns = 0;
utctx->first_time_ns = 0;
}
if (chunk->eof) {
fprintf(utctx->out, "END OF FRAME %u\n", utctx->frame_nr++);
}
}
static void
cleanup_chunk(void *job, int thread_index)
{
ralloc_free(job);
}
void
u_trace_context_process(struct u_trace_context *utctx, bool eof)
{
struct list_head *chunks = &utctx->flushed_trace_chunks;
if (list_is_empty(chunks))
return;
struct u_trace_chunk *last_chunk = list_last_entry(chunks,
struct u_trace_chunk, node);
last_chunk->eof = eof;
while (!list_is_empty(chunks)) {
struct u_trace_chunk *chunk = list_first_entry(chunks,
struct u_trace_chunk, node);
/* remove from list before enqueuing, because chunk is freed
* once it is processed by the queue:
*/
list_delinit(&chunk->node);
util_queue_add_job(&utctx->queue, chunk, &chunk->fence,
process_chunk, cleanup_chunk,
TIMESTAMP_BUF_SIZE);
}
}
void
u_trace_init(struct u_trace *ut, struct u_trace_context *utctx)
{
ut->utctx = utctx;
list_inithead(&ut->trace_chunks);
ut->enabled = !!utctx->out;
}
void
u_trace_fini(struct u_trace *ut)
{
/* Normally the list of trace-chunks would be empty, if they
* have been flushed to the trace-context.
*/
free_chunks(&ut->trace_chunks);
}
/**
* Append a trace event, returning pointer to buffer of tp->payload_sz
* to be filled in with trace payload. Called by generated tracepoint
* functions.
*/
void *
u_trace_append(struct u_trace *ut, const struct u_tracepoint *tp)
{
struct u_trace_chunk *chunk = get_chunk(ut);
assert(tp->payload_sz == ALIGN_NPOT(tp->payload_sz, 8));
if (unlikely((chunk->payload_buf + tp->payload_sz) > chunk->payload_end)) {
const unsigned payload_chunk_sz = 0x100; /* TODO arbitrary size? */
assert(tp->payload_sz < payload_chunk_sz);
chunk->payload_buf = ralloc_size(chunk, payload_chunk_sz);
chunk->payload_end = chunk->payload_buf + payload_chunk_sz;
}
/* sub-allocate storage for trace payload: */
void *payload = chunk->payload_buf;
chunk->payload_buf += tp->payload_sz;
/* record a timestamp for the trace: */
ut->utctx->record_timestamp(ut, chunk->timestamps, chunk->num_traces);
chunk->traces[chunk->num_traces] = (struct u_trace_event) {
.tp = tp,
.payload = payload,
};
chunk->num_traces++;
return payload;
}
void
u_trace_flush(struct u_trace *ut)
{
/* transfer batch's log chunks to context: */
list_splicetail(&ut->trace_chunks, &ut->utctx->flushed_trace_chunks);
list_inithead(&ut->trace_chunks);
}

View File

@ -0,0 +1,208 @@
/*
* Copyright © 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.
*/
#ifndef _U_TRACE_H
#define _U_TRACE_H
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "util/u_queue.h"
/* A trace mechanism (very) loosely inspired by the linux kernel tracepoint
* mechanism, in that it allows for defining driver specific (or common)
* tracepoints, which generate 'trace_$name()' functions that can be
* called at various points in commandstream emit.
*
* Currently a printf backend is implemented, but the expectation is to
* also implement a perfetto backend for shipping out traces to a tool like
* AGI.
*
* Notable differences:
*
* - GPU timestamps! A driver provided callback is used to emit timestamps
* to a buffer. At a later point in time (when stalling to wait for the
* GPU is not required), the timestamps are re-united with the trace
* payload. This makes the trace mechanism suitable for profiling.
*
* - Instead of a systemwide trace ringbuffer, buffering of un-retired
* tracepoints is split into two stages. Traces are emitted to a
* 'u_trace' instance, and at a later time flushed to a 'u_trace_context'
* instance. This avoids the requirement that commandstream containing
* tracepoints is emitted in the same order as it is generated.
*
* If the hw has multiple parallel "engines" (for example, 3d/blit/compute)
* then a `u_trace_context` per-engine should be used.
*
* - Unlike kernel tracepoints, u_trace tracepoints are defined in py
* from which header and src files are generated. Since we already have
* a build dependency on python+mako, this gives more flexibility than
* clunky preprocessor macro magic.
*
*/
struct u_trace_context;
struct u_trace;
struct u_trace_chunk;
struct pipe_resource;
/**
* Special reserved value to indicate that no timestamp was captured,
* and that the timestamp of the previous trace should be reused.
*/
#define U_TRACE_NO_TIMESTAMP ((uint64_t)0)
/**
* Driver provided callback to emit commands to capture a 64b timestamp
* into the specified timestamps buffer, at the specified index.
*
* The hw counter that the driver records should be something that runs at
* a fixed rate, even as the GPU freq changes. The same source used for
* GL_TIMESTAMP queries should be appropriate.
*/
typedef void (*u_trace_record_ts)(struct u_trace *ut,
struct pipe_resource *timestamps, unsigned idx);
/**
* Driver provided callback to read back a previously recorded timestamp.
* If necessary, this should block until the GPU has finished writing back
* the timestamps. (The timestamps will be read back in order, so it is
* safe to only synchronize on idx==0.)
*
* The returned timestamp should be in units of nanoseconds. The same
* timebase as GL_TIMESTAMP queries should be used.
*
* The driver can return the special U_TRACE_NO_TIMESTAMP value to indicate
* that no timestamp was captured and the timestamp from the previous trace
* will be re-used. (The first trace in the u_trace buf may not do this.)
* This allows the driver to detect cases where multiple tracepoints are
* emitted with no other intervening cmdstream, to avoid pointlessly
* capturing the same timestamp multiple times in a row.
*/
typedef uint64_t (*u_trace_read_ts)(struct u_trace_context *utctx,
struct pipe_resource *timestamps, unsigned idx);
/**
* The trace context provides tracking for "in-flight" traces, once the
* cmdstream that records timestamps has been flushed.
*/
struct u_trace_context {
struct pipe_context *pctx;
u_trace_record_ts record_timestamp;
u_trace_read_ts read_timestamp;
FILE *out;
/* Once u_trace_flush() is called u_trace_chunk's are queued up to
* render tracepoints on a queue. The per-chunk queue jobs block until
* timestamps are available.
*/
struct util_queue queue;
/* State to accumulate time across N chunks associated with a single
* batch (u_trace).
*/
uint64_t last_time_ns;
uint64_t first_time_ns;
uint32_t frame_nr;
/* list of unprocessed trace chunks in fifo order: */
struct list_head flushed_trace_chunks;
};
/**
* The u_trace ptr is passed as the first arg to generated tracepoints.
* It provides buffering for tracepoint payload until the corresponding
* driver cmdstream containing the emitted commands to capture is
* flushed.
*
* Individual tracepoints emitted to u_trace are expected to be "executed"
* (ie. timestamp captured) in FIFO order with respect to other tracepoints
* emitted to the same u_trace. But the order WRT other u_trace instances
* is undefined util u_trace_flush().
*/
struct u_trace {
struct u_trace_context *utctx;
struct list_head trace_chunks; /* list of unflushed trace chunks in fifo order */
bool enabled;
};
void u_trace_context_init(struct u_trace_context *utctx,
struct pipe_context *pctx,
u_trace_record_ts record_timestamp,
u_trace_read_ts read_timestamp);
void u_trace_context_fini(struct u_trace_context *utctx);
/**
* Flush (trigger processing) of traces previously flushed to the trace-context
* by u_trace_flush().
*
* This should typically be called in the driver's pctx->flush().
*/
void u_trace_context_process(struct u_trace_context *utctx, bool eof);
void u_trace_init(struct u_trace *ut, struct u_trace_context *utctx);
void u_trace_fini(struct u_trace *ut);
/**
* Flush traces to the parent trace-context. At this point, the expectation
* is that all the tracepoints are "executed" by the GPU following any previously
* flushed u_trace batch.
*
* This should typically be called when the corresponding cmdstream (containing
* the timestamp reads) is flushed to the kernel.
*/
void u_trace_flush(struct u_trace *ut);
/*
* TODO in some cases it is useful to have composite tracepoints like this,
* to log more complex data structures.. but this is probably not where they
* should live:
*/
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);
static inline void
trace_framebuffer_state(struct u_trace *ut, const struct pipe_framebuffer_state *pfb)
{
if (likely(!ut->enabled))
return;
__trace_framebuffer(ut, pfb);
for (unsigned i = 0; i < pfb->nr_cbufs; i++) {
if (pfb->cbufs[i]) {
__trace_surface(ut, pfb->cbufs[i]);
}
}
if (pfb->zsbuf) {
__trace_surface(ut, pfb->zsbuf);
}
}
#endif /* _U_TRACE_H */

View File

@ -0,0 +1,219 @@
#
# 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
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):
"""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 [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])
- tp_print: (optional) array of format string followed by expressions
"""
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 = []
for arg in args:
tp_struct.append([arg[0], arg[1], arg[1]])
self.tp_struct = tp_struct
self.tp_print = tp_print
TRACEPOINTS[name] = self
HEADERS = []
class Header(object):
"""Class that represents a header file dependency of generated tracepoints
"""
def __init__(self, hdr):
"""Parameters:
- hdr: the required header path
"""
assert isinstance(hdr, str)
self.hdr = hdr
HEADERS.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/u_trace.h"
% for trace_name, trace in TRACEPOINTS.items():
void __trace_${trace_name}(struct u_trace *ut
% for arg in trace.args:
, ${arg[0]} ${arg[1]}
% endfor
);
static inline void trace_${trace_name}(struct u_trace *ut
% for arg in trace.args:
, ${arg[0]} ${arg[1]}
% endfor
) {
if (likely(!ut->enabled))
return;
__trace_${trace_name}(ut
% for arg in trace.args:
, ${arg[1]}
% endfor
);
}
% endfor
#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/u_trace_priv.h"
% for trace_name, trace in TRACEPOINTS.items():
/*
* ${trace_name}
*/
struct __payload_${trace_name} {
% for member in trace.tp_struct:
${member[0]} ${member[1]};
% endfor
};
% if trace.tp_print is not None:
static void __print_${trace_name}(FILE *out, const void *arg) {
const struct __payload_${trace_name} *__entry =
(const struct __payload_${trace_name} *)arg;
fprintf(out, "${trace.tp_print[0]}\\n"
% for arg in trace.tp_print[1:]:
, ${arg}
% endfor
);
}
% else:
#define __print_${trace_name} NULL
% endif
static const struct u_tracepoint __tp_${trace_name} = {
ALIGN_POT(sizeof(struct __payload_${trace_name}), 8), /* keep size 64b aligned */
"${trace_name}",
__print_${trace_name},
};
void __trace_${trace_name}(struct u_trace *ut
% for arg in trace.args:
, ${arg[0]} ${arg[1]}
% endfor
) {
struct __payload_${trace_name} *__entry =
(struct __payload_${trace_name} *)u_trace_append(ut, &__tp_${trace_name});
(void)__entry;
% for member in trace.tp_struct:
__entry->${member[1]} = ${member[2]};
% endfor
}
% endfor
"""
def utrace_generate(cpath, hpath):
hdr = os.path.basename(hpath)
with open(cpath, 'wb') as f:
f.write(Template(src_template, output_encoding='utf-8').render(
hdr=hdr,
HEADERS=HEADERS,
TRACEPOINTS=TRACEPOINTS))
with open(hpath, 'wb') as f:
f.write(Template(hdr_template, output_encoding='utf-8').render(
hdrname=hdr.rstrip('.h').upper(),
HEADERS=HEADERS,
TRACEPOINTS=TRACEPOINTS))

View File

@ -0,0 +1,54 @@
/*
* Copyright © 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.
*/
#ifndef __NEEDS_TRACE_PRIV
# error "Do not use this header!"
#endif
#ifndef _U_TRACE_PRIV_H
#define _U_TRACE_PRIV_H
#include <stdio.h>
#include "u_trace.h"
/*
* Internal interface used by generated tracepoints
*/
/**
* Tracepoint descriptor.
*/
struct u_tracepoint {
unsigned payload_sz;
const char *name;
void (*print)(FILE *out, const void *payload);
};
/**
* Append a tracepoint, returning pointer that can be filled with trace
* payload.
*/
void * u_trace_append(struct u_trace *ut, const struct u_tracepoint *tp);
#endif /* _U_TRACE_PRIV_H */

View File

@ -0,0 +1,92 @@
#
# 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.
#
import argparse
import sys
#
# TODO can we do this with less boilerplate?
#
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--import-path', required=True)
parser.add_argument('-C', '--src', required=True)
parser.add_argument('-H', '--hdr', required=True)
args = parser.parse_args()
sys.path.insert(0, args.import_path)
from u_trace import Header
from u_trace import Tracepoint
from u_trace import utrace_generate
#
# Tracepoint definitions:
#
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)']],
tp_print=['%ux%u@%u, fmt=%s',
'__entry->width',
'__entry->height',
'__entry->nr_samples',
'__entry->format'],
)
# 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']],
tp_print=['%ux%ux%u@%u, nr_cbufs: %u',
'__entry->width',
'__entry->height',
'__entry->layers',
'__entry->samples',
'__entry->nr_cbufs'],
)
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]']],
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'],
)
utrace_generate(cpath=args.src, hpath=args.hdr)