u_trace: add support for indirect data

Allows a driver to declare indirect arguments for its tracepoints and
pass an address. u_trace will request a copy of the data which should
be implemented on the command processor.

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Co-Authored-by: Danylo Piliaiev <dpiliaiev@igalia.com>
Reviewed-by: Danylo Piliaiev <dpiliaiev@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/29944>
This commit is contained in:
Lionel Landwerlin
2024-06-26 20:37:28 +03:00
committed by Lionel Landwerlin
parent cb27b9541b
commit 0a17035b5c
18 changed files with 333 additions and 80 deletions
+69 -8
View File
@@ -91,6 +91,10 @@ struct u_trace_chunk {
*/
void *timestamps;
/* table of indirect data captured by u_trace
*/
void *indirects;
/* Array of u_trace_payload_buf referenced by traces[] elements.
*/
struct u_vector payloads;
@@ -100,6 +104,7 @@ struct u_trace_chunk {
struct util_queue_fence fence;
bool has_indirect;
bool last; /* this chunk is last in batch */
bool eof; /* this chunk is last in frame, unless frame_nr is set */
uint32_t frame_nr; /* frame idx from the driver */
@@ -124,7 +129,8 @@ struct u_trace_printer {
struct u_trace_chunk *chunk,
const struct u_trace_event *evt,
uint64_t ns,
int32_t delta);
int32_t delta,
const void *indirect);
};
static void
@@ -156,12 +162,13 @@ print_txt_event(struct u_trace_context *utctx,
struct u_trace_chunk *chunk,
const struct u_trace_event *evt,
uint64_t ns,
int32_t delta)
int32_t delta,
const void *indirect)
{
if (evt->tp->print) {
fprintf(utctx->out, "%016" PRIu64 " %+9d: %s: ", ns, delta,
evt->tp->name);
evt->tp->print(utctx->out, evt->payload);
evt->tp->print(utctx->out, evt->payload, indirect);
} else {
fprintf(utctx->out, "%016" PRIu64 " %+9d: %s\n", ns, delta,
evt->tp->name);
@@ -228,7 +235,8 @@ print_json_event(struct u_trace_context *utctx,
struct u_trace_chunk *chunk,
const struct u_trace_event *evt,
uint64_t ns,
int32_t delta)
int32_t delta,
const void *indirect)
{
if (utctx->event_nr != 0)
fprintf(utctx->out, ",\n");
@@ -236,7 +244,7 @@ print_json_event(struct u_trace_context *utctx,
fprintf(utctx->out, "\"time_ns\": \"%016" PRIu64 "\",\n", ns);
fprintf(utctx->out, "\"params\": {");
if (evt->tp->print)
evt->tp->print_json(utctx->out, evt->payload);
evt->tp->print_json(utctx->out, evt->payload, indirect);
fprintf(utctx->out, "}\n}\n");
}
@@ -285,6 +293,8 @@ free_chunk(void *ptr)
struct u_trace_chunk *chunk = ptr;
chunk->utctx->delete_buffer(chunk->utctx, chunk->timestamps);
if (chunk->indirects)
chunk->utctx->delete_buffer(chunk->utctx, chunk->indirects);
/* Unref payloads attached to this chunk. */
struct u_trace_payload_buf **payload;
@@ -349,6 +359,12 @@ get_chunk(struct u_trace *ut, size_t payload_size)
chunk->timestamps =
ut->utctx->create_buffer(ut->utctx,
chunk->utctx->timestamp_size_bytes * TIMESTAMP_BUF_SIZE);
if (chunk->utctx->max_indirect_size_bytes &&
(chunk->utctx->enabled_traces & U_TRACE_TYPE_INDIRECTS)) {
chunk->indirects =
ut->utctx->create_buffer(ut->utctx,
chunk->utctx->max_indirect_size_bytes * TIMESTAMP_BUF_SIZE);
}
chunk->last = true;
u_vector_init(&chunk->payloads, 4, sizeof(struct u_trace_payload_buf *));
if (payload_size > 0) {
@@ -369,6 +385,7 @@ static const struct debug_named_value config_control[] = {
{ "perfetto", U_TRACE_TYPE_PERFETTO_ENV, "Enable perfetto" },
#endif
{ "markers", U_TRACE_TYPE_MARKERS, "Enable marker trace" },
{ "indirects", U_TRACE_TYPE_INDIRECTS, "Enable indirect data capture" },
DEBUG_NAMED_VALUE_END
};
@@ -436,10 +453,13 @@ void
u_trace_context_init(struct u_trace_context *utctx,
void *pctx,
uint32_t timestamp_size_bytes,
uint32_t max_indirect_size_bytes,
u_trace_create_buffer create_buffer,
u_trace_delete_buffer delete_buffer,
u_trace_record_ts record_timestamp,
u_trace_read_ts read_timestamp,
u_trace_capture_data capture_data,
u_trace_get_data get_data,
u_trace_delete_flush_data delete_flush_data)
{
u_trace_state_init();
@@ -449,9 +469,12 @@ u_trace_context_init(struct u_trace_context *utctx,
utctx->create_buffer = create_buffer;
utctx->delete_buffer = delete_buffer;
utctx->record_timestamp = record_timestamp;
utctx->capture_data = capture_data;
utctx->get_data = get_data;
utctx->read_timestamp = read_timestamp;
utctx->delete_flush_data = delete_flush_data;
utctx->timestamp_size_bytes = timestamp_size_bytes;
utctx->max_indirect_size_bytes = max_indirect_size_bytes;
utctx->last_time_ns = 0;
utctx->first_time_ns = 0;
@@ -460,6 +483,8 @@ u_trace_context_init(struct u_trace_context *utctx,
utctx->event_nr = 0;
utctx->start_of_frame = true;
utctx->dummy_indirect_data = calloc(1, max_indirect_size_bytes);
list_inithead(&utctx->flushed_trace_chunks);
if (utctx->enabled_traces & U_TRACE_TYPE_PRINT) {
@@ -515,6 +540,8 @@ u_trace_context_fini(struct u_trace_context *utctx)
fflush(utctx->out);
}
free (utctx->dummy_indirect_data);
if (!utctx->queue.jobs)
return;
util_queue_finish(&utctx->queue);
@@ -614,14 +641,26 @@ process_chunk(void *job, void *gdata, int thread_index)
delta = 0;
}
const void *indirect_data = NULL;
if (evt->tp->indirect_sz > 0) {
if (utctx->enabled_traces & U_TRACE_TYPE_INDIRECTS) {
indirect_data = utctx->get_data(utctx, chunk->indirects,
utctx->max_indirect_size_bytes * idx,
evt->tp->indirect_sz);
} else {
indirect_data = utctx->dummy_indirect_data;
}
}
if (utctx->out) {
utctx->out_printer->event(utctx, chunk, evt, ns, delta);
utctx->out_printer->event(utctx, chunk, evt, ns, delta, indirect_data);
}
#ifdef HAVE_PERFETTO
if (evt->tp->perfetto &&
(p_atomic_read_relaxed(&utctx->enabled_traces) &
U_TRACE_TYPE_PERFETTO_ACTIVE)) {
evt->tp->perfetto(utctx->pctx, ns, evt->tp->tp_idx, chunk->flush_data, evt->payload);
evt->tp->perfetto(utctx->pctx, ns, evt->tp->tp_idx, chunk->flush_data,
evt->payload, indirect_data);
}
#endif
@@ -782,6 +821,15 @@ u_trace_clone_append(struct u_trace_iterator begin_it,
begin_it.ut->utctx->timestamp_size_bytes * to_chunk->num_traces,
begin_it.ut->utctx->timestamp_size_bytes * to_copy);
if (from_chunk->has_indirect) {
copy_buffer(begin_it.ut->utctx, cmdstream,
from_chunk->indirects,
begin_it.ut->utctx->max_indirect_size_bytes * from_idx,
to_chunk->indirects,
begin_it.ut->utctx->max_indirect_size_bytes * to_chunk->num_traces,
begin_it.ut->utctx->max_indirect_size_bytes * to_copy);
}
memcpy(&to_chunk->traces[to_chunk->num_traces],
&from_chunk->traces[from_idx],
to_copy * sizeof(struct u_trace_event));
@@ -845,7 +893,10 @@ void *
u_trace_appendv(struct u_trace *ut,
void *cs,
const struct u_tracepoint *tp,
unsigned variable_sz)
unsigned variable_sz,
unsigned n_indirects,
const struct u_trace_address *addresses,
const uint8_t *indirect_sizes_B)
{
assert(tp->payload_sz == ALIGN_NPOT(tp->payload_sz, 8));
@@ -865,6 +916,16 @@ u_trace_appendv(struct u_trace *ut,
ut->utctx->timestamp_size_bytes * tp_idx,
tp->flags);
if (ut->utctx->enabled_traces & U_TRACE_TYPE_INDIRECTS) {
for (unsigned i = 0; i < n_indirects; i++) {
ut->utctx->capture_data(ut, cs, chunk->indirects,
ut->utctx->max_indirect_size_bytes * tp_idx,
addresses[i].bo, addresses[i].offset,
indirect_sizes_B[i]);
}
chunk->has_indirect |= n_indirects > 0;
}
chunk->traces[tp_idx] = (struct u_trace_event) {
.tp = tp,
.payload = payload,
+53
View File
@@ -79,6 +79,20 @@ struct u_trace_printer;
*/
#define U_TRACE_NO_TIMESTAMP ((uint64_t) 0)
/**
* Address representation
*/
struct u_trace_address {
/**
* Pointer to a buffer object
*/
void *bo;
/**
* Offset inside the buffer object or address of bo is NULL
*/
uint64_t offset;
};
/**
* Driver provided callback to create a buffer which will be read by
* u_trace_read_ts function.
@@ -107,6 +121,24 @@ typedef void (*u_trace_record_ts)(struct u_trace *ut,
uint64_t offset_B,
uint32_t flags);
/**
* Driver provided callback to capture indirect data.
*/
typedef void (*u_trace_capture_data)(struct u_trace *ut,
void *cs,
void *dst_buffer,
uint64_t dst_offset_B,
void *src_buffer,
uint64_t src_offset_B,
uint32_t size_B);
/**
* Driver provided callback to read back previously recorded indirect data.
*/
typedef const void *(*u_trace_get_data)(struct u_trace_context *utctx,
void *buffer,
uint64_t offset_B,
uint32_t size_B);
/**
* Driver provided callback to read back a previously recorded timestamp.
* If necessary, this should block until the GPU has finished writing back
@@ -130,6 +162,18 @@ typedef uint64_t (*u_trace_read_ts)(struct u_trace_context *utctx,
uint64_t offset_B,
void *flush_data);
/**
* Driver provided callback to create a buffer which will be read by
* u_trace_read_ts function.
*/
typedef void *(*u_trace_copy_data)(struct u_trace *ut,
void *cs,
void *dst,
uint64_t dst_offset_B,
void *src,
uint64_t src_offset_B,
uint64_t size_B);
/**
* Driver provided callback to delete flush data.
*/
@@ -142,6 +186,7 @@ enum u_trace_type {
U_TRACE_TYPE_PERFETTO_ACTIVE = 1u << 2,
U_TRACE_TYPE_PERFETTO_ENV = 1u << 3,
U_TRACE_TYPE_MARKERS = 1u << 4,
U_TRACE_TYPE_INDIRECTS = 1u << 5,
U_TRACE_TYPE_PRINT_JSON = U_TRACE_TYPE_PRINT | U_TRACE_TYPE_JSON,
U_TRACE_TYPE_PERFETTO =
@@ -170,11 +215,14 @@ struct u_trace_context {
u_trace_create_buffer create_buffer;
u_trace_delete_buffer delete_buffer;
u_trace_capture_data capture_data;
u_trace_get_data get_data;
u_trace_record_ts record_timestamp;
u_trace_read_ts read_timestamp;
u_trace_delete_flush_data delete_flush_data;
uint64_t timestamp_size_bytes;
uint64_t max_indirect_size_bytes;
FILE *out;
struct u_trace_printer *out_printer;
@@ -201,6 +249,8 @@ struct u_trace_context {
uint32_t event_nr;
bool start_of_frame;
void *dummy_indirect_data;
/* list of unprocessed trace chunks in fifo order: */
struct list_head flushed_trace_chunks;
};
@@ -228,10 +278,13 @@ struct u_trace {
void u_trace_context_init(struct u_trace_context *utctx,
void *pctx,
uint32_t timestamp_size_bytes,
uint32_t max_indirect_size_bytes,
u_trace_create_buffer create_buffer,
u_trace_delete_buffer delete_buffer,
u_trace_record_ts record_timestamp,
u_trace_read_ts read_timestamp,
u_trace_capture_data capture_data,
u_trace_get_data get_data,
u_trace_delete_flush_data delete_flush_data);
void u_trace_context_fini(struct u_trace_context *utctx);
+78 -17
View File
@@ -59,6 +59,8 @@ class Tracepoint(object):
def needs_storage(a):
if a.c_format is None:
return False
if a.is_indirect:
return False
return True
self.name = name
@@ -81,6 +83,14 @@ class Tracepoint(object):
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
@@ -105,7 +115,7 @@ class Tracepoint(object):
class TracepointArgStruct():
"""Represents struct that is being passed as an argument
"""
def __init__(self, type, var, c_format=None, fields=[]):
def __init__(self, type, var, c_format=None, fields=[], is_indirect=False):
"""Parameters:
- type: argument's C type.
@@ -117,17 +127,25 @@ 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.func_param = f"{self.type} {self.var}"
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:
ret = ", ".join([f"{entry_name}->{self.name}.{f}" for f in self.fields])
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
@@ -136,7 +154,7 @@ class TracepointArg(object):
"""Class that represents either an argument being passed or a field in a struct
"""
def __init__(self, type, var, c_format=None, name=None, to_prim_type=None,
length_arg=None, copy_func=None):
length_arg=None, copy_func=None, is_indirect=False):
"""Parameters:
- type: argument's C type.
@@ -162,8 +180,12 @@ class TracepointArg(object):
self.copy_func = copy_func
self.is_struct = False
self.is_indirect = is_indirect
self.indirect_offset = 0
if self.type == "str":
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:
@@ -171,13 +193,18 @@ class TracepointArg(object):
else:
self.struct_member = f"{self.type} {self.name}"
if self.type == "str":
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):
ret = f"{entry_name}->{self.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
@@ -298,7 +325,8 @@ void ${trace.tp_perfetto}(
uint64_t ts_ns,
uint16_t tp_idx,
const void *flush_data,
const struct trace_${trace_name} *payload);
const struct trace_${trace_name} *payload,
const void *indirect_data);
#endif
% endif
void __trace_${trace_name}(
@@ -416,11 +444,14 @@ ${trace_toggle_name}_config_variable(void)
* ${trace_name}
*/
% if trace.can_generate_print():
static void __print_${trace_name}(FILE *out, const void *arg) {
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;
% 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:]:
@@ -439,11 +470,14 @@ static void __print_${trace_name}(FILE *out, const void *arg) {
);
}
static void __print_json_${trace_name}(FILE *out, const void *arg) {
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:]:
@@ -475,26 +509,35 @@ __attribute__((format(printf, 3, 4))) void ${trace.tp_markers}(struct u_trace_co
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},
__print_json_${trace_name},
% if trace.tp_perfetto is not None:
#ifdef HAVE_PERFETTO
(void (*)(void *pctx, uint64_t, uint16_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
};
@@ -509,9 +552,20 @@ void __trace_${trace_name}(
% 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 ?
% if trace.has_variable_arg:
(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:
@@ -519,10 +573,13 @@ void __trace_${trace_name}(
+ ${arg.length_arg}
% endif
% endfor
,
% if len(trace.indirect_args) > 0:
ARRAY_SIZE(indirects), indirects, indirect_sizes
% else:
0, NULL, NULL
% endif
) :
% else:
(struct trace_${trace_name} *)u_trace_append(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name}) :
% endif
&entry;
% for arg in trace.tp_struct:
% if arg.copy_func is None:
@@ -625,7 +682,8 @@ UNUSED static const char *${basename}_names[] = {
% 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 trace.tp_perfetto is not None and len(trace.tp_print) > 0:
char buf[128];
@@ -635,6 +693,9 @@ trace_payload_as_extra_${trace_name}(perfetto::protos::pbzero::GpuRenderStageEve
auto data = event->add_extra_data();
data->set_name("${arg.name}");
% 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);
+17 -6
View File
@@ -43,8 +43,15 @@ extern "C" {
* Tracepoint descriptor.
*/
struct u_tracepoint {
unsigned payload_sz;
const char *name;
/**
* Size of the CPU data associated with this tracepoint.
*/
uint16_t payload_sz;
/**
* Size of the GPU data associated with this tracepoint.
*/
uint16_t indirect_sz;
/**
* A bitfield of driver agnostic flags
*/
@@ -56,8 +63,8 @@ struct u_tracepoint {
* to event->set_stage_iid().
*/
uint16_t tp_idx;
void (*print)(FILE *out, const void *payload);
void (*print_json)(FILE *out, const void *payload);
void (*print)(FILE *out, const void *payload, const void *indirect);
void (*print_json)(FILE *out, const void *payload, const void *indirect);
#ifdef HAVE_PERFETTO
/**
* Callback to emit a perfetto event, such as render-stage trace
@@ -66,7 +73,8 @@ struct u_tracepoint {
uint64_t ts_ns,
uint16_t tp_idx,
const void *flush_data,
const void *payload);
const void *payload,
const void *indirect);
#endif
};
@@ -77,7 +85,10 @@ struct u_tracepoint {
void *u_trace_appendv(struct u_trace *ut,
void *cs,
const struct u_tracepoint *tp,
unsigned variable_sz);
unsigned variable_sz,
unsigned n_indirects,
const struct u_trace_address *addresses,
const uint8_t *indirect_sizes_B);
/**
* Append a trace event, returning pointer to buffer of tp->payload_sz
@@ -87,7 +98,7 @@ void *u_trace_appendv(struct u_trace *ut,
static inline void *
u_trace_append(struct u_trace *ut, void *cs, const struct u_tracepoint *tp)
{
return u_trace_appendv(ut, cs, tp, 0);
return u_trace_appendv(ut, cs, tp, 0, 0, NULL, NULL);
}
#ifdef __cplusplus
+2 -1
View File
@@ -11,7 +11,8 @@ static int
test_thread(void *_state)
{
struct u_trace_context ctx = {};
u_trace_context_init(&ctx, NULL, NULL, NULL, NULL, NULL, NULL);
u_trace_context_init(&ctx, NULL, 8, 0, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
u_trace_context_fini(&ctx);
return 0;