#define TIMESTAMP_BUF_SIZE 0x1000
#define TRACES_PER_CHUNK (TIMESTAMP_BUF_SIZE / sizeof(uint64_t))
+#ifdef HAVE_PERFETTO
+int ut_perfetto_enabled;
+
+/**
+ * Global list of contexts, so we can defer starting the queue until
+ * perfetto tracing is started.
+ *
+ * TODO locking
+ */
+struct list_head ctx_list = { &ctx_list, &ctx_list };
+#endif
+
struct u_trace_event {
const struct u_tracepoint *tp;
const void *payload;
return tracefile;
}
+static void
+queue_init(struct u_trace_context *utctx)
+{
+ if (utctx->queue.jobs)
+ return;
+
+ bool ret = util_queue_init(&utctx->queue, "traceq", 256, 1,
+ UTIL_QUEUE_INIT_USE_MINIMUM_PRIORITY |
+ UTIL_QUEUE_INIT_RESIZE_IF_FULL);
+ assert(ret);
+
+ if (!ret)
+ utctx->out = NULL;
+}
+
void
u_trace_context_init(struct u_trace_context *utctx,
struct pipe_context *pctx,
utctx->out = get_tracefile();
- if (!utctx->out)
- return;
+#ifdef HAVE_PERFETTO
+ list_add(&utctx->node, &ctx_list);
+#endif
- bool ret = util_queue_init(&utctx->queue, "traceq", 256, 1,
- UTIL_QUEUE_INIT_USE_MINIMUM_PRIORITY |
- UTIL_QUEUE_INIT_RESIZE_IF_FULL);
- assert(ret);
+ if (!(utctx->out || ut_perfetto_enabled))
+ return;
- if (!ret)
- utctx->out = NULL;
+ queue_init(utctx);
}
void
u_trace_context_fini(struct u_trace_context *utctx)
{
+#ifdef HAVE_PERFETTO
+ list_del(&utctx->node);
+#endif
if (!utctx->out)
return;
util_queue_finish(&utctx->queue);
free_chunks(&utctx->flushed_trace_chunks);
}
+#ifdef HAVE_PERFETTO
+void
+u_trace_perfetto_start(void)
+{
+ list_for_each_entry (struct u_trace_context, utctx, &ctx_list, node)
+ queue_init(utctx);
+ ut_perfetto_enabled++;
+}
+
+void
+u_trace_perfetto_stop(void)
+{
+ assert(ut_perfetto_enabled > 0);
+ ut_perfetto_enabled--;
+}
+#endif
+
static void
process_chunk(void *job, int thread_index)
{
struct u_trace_context *utctx = chunk->utctx;
/* For first chunk of batch, accumulated times will be zerod: */
- if (!utctx->last_time_ns) {
+ if (utctx->out && !utctx->last_time_ns) {
fprintf(utctx->out, "+----- NS -----+ +-- Δ --+ +----- MSG -----\n");
}
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 (utctx->out) {
+ 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);
+ }
}
+#ifdef HAVE_PERFETTO
+ if (evt->tp->perfetto) {
+ evt->tp->perfetto(utctx->pctx, ns, evt->payload);
+ }
+#endif
}
if (chunk->last) {
- uint64_t elapsed = utctx->last_time_ns - utctx->first_time_ns;
- fprintf(utctx->out, "ELAPSED: %"PRIu64" ns\n", elapsed);
+ if (utctx->out) {
+ 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) {
+ if (utctx->out && chunk->eof) {
fprintf(utctx->out, "END OF FRAME %u\n", utctx->frame_nr++);
}
}
#include "util/u_queue.h"
+#include "pipe/p_context.h"
+#include "pipe/p_state.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* 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
*/
struct util_queue queue;
+#ifdef HAVE_PERFETTO
+ /* node in global list of trace contexts. */
+ struct list_head node;
+#endif
+
/* State to accumulate time across N chunks associated with a single
* batch (u_trace).
*/
*/
void u_trace_flush(struct u_trace *ut);
+#ifdef HAVE_PERFETTO
+extern int ut_perfetto_enabled;
+
+void u_trace_perfetto_start(void);
+void u_trace_perfetto_stop(void);
+#else
+# define ut_perfetto_enabled 0
+#endif
+
/*
* 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
}
}
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _U_TRACE_H */
class Tracepoint(object):
"""Class that represents all the information about a tracepoint
"""
- def __init__(self, name, args=[], tp_struct=None, tp_print=None):
+ 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
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
+ - tp_perfetto: (optional) driver provided callback which can generate
+ perfetto events
"""
assert isinstance(name, str)
assert isinstance(args, list)
tp_struct.append([arg[0], arg[1], arg[1]])
self.tp_struct = tp_struct
self.tp_print = tp_print
+ self.tp_perfetto = tp_perfetto
TRACEPOINTS[name] = self
#include "util/u_trace.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
% for trace_name, trace in TRACEPOINTS.items():
+/*
+ * ${trace_name}
+ */
+struct trace_${trace_name} {
+% for member in trace.tp_struct:
+ ${member[0]} ${member[1]};
+% endfor
+% 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}(struct pipe_context *pctx, uint64_t ts_ns, const struct trace_${trace_name} *payload);
+#endif
+% endif
void __trace_${trace_name}(struct u_trace *ut
% for arg in trace.args:
, ${arg[0]} ${arg[1]}
, ${arg[0]} ${arg[1]}
% endfor
) {
- if (likely(!ut->enabled))
+% 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
% for arg in trace.args:
}
% endfor
+#ifdef __cplusplus
+}
+#endif
+
#endif /* ${guard_name} */
"""
/*
* ${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;
+ const struct trace_${trace_name} *__entry =
+ (const struct trace_${trace_name} *)arg;
fprintf(out, "${trace.tp_print[0]}\\n"
% for arg in trace.tp_print[1:]:
, ${arg}
#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 */
+ 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 (*)(struct pipe_context *, uint64_t, const void *))${trace.tp_perfetto},
+#endif
+% endif
};
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});
+ 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]};
unsigned payload_sz;
const char *name;
void (*print)(FILE *out, const void *payload);
+#ifdef HAVE_PERFETTO
+ /**
+ * Callback to emit a perfetto event, such as render-stage trace
+ */
+ void (*perfetto)(struct pipe_context *pctx, uint64_t ts_ns, const void *payload);
+#endif
};
/**