diff --git a/src/intel/ds/intel_driver_ds.cc b/src/intel/ds/intel_driver_ds.cc index 75438d4d2f4..7d0b287f620 100644 --- a/src/intel/ds/intel_driver_ds.cc +++ b/src/intel/ds/intel_driver_ds.cc @@ -23,6 +23,7 @@ #include #include +#include #include "common/intel_gem.h" #include "perf/intel_perf.h" @@ -307,6 +308,74 @@ end_event(struct intel_ds_queue *queue, uint64_t ts_ns, stage->start_ns[level] = 0; } +/* Variant for dynamic event names (for example, names formatted in + * stack-local buffers). + * + * Takes a std::string by value so the [=] lambda capture copies string storage + * into the Trace() closure. This avoids dangling pointers when Trace() runs + * after the caller's stack frame is gone. + * + * Keep end_event() with const char* for NULL, string literals, and other + * long-lived pointers to avoid std::string construction on the common path. + */ +static void +end_event_dyn(struct intel_ds_queue *queue, uint64_t ts_ns, + enum intel_ds_queue_stage stage_id, + uint32_t submission_id, + uint16_t tracepoint_idx, + std::string app_event, + const void *payload = nullptr, + const void *indirect_data = nullptr, + trace_payload_as_extra_func payload_as_extra = nullptr) +{ + struct intel_ds_device *device = queue->device; + + if (queue->stages[stage_id].level == 0) + return; + + uint32_t level = --queue->stages[stage_id].level; + struct intel_ds_stage *stage = &queue->stages[stage_id]; + uint64_t start_ns = stage->start_ns[level]; + + if (!start_ns) + return; + + IntelRenderpassDataSource::Trace([=](IntelRenderpassDataSource::TraceContext tctx) { + setup_incremental_state(tctx, device); + + sync_timestamp(tctx, device); + + uint64_t evt_id = device->event_id++; + + uint64_t stage_iid = !app_event.empty() ? + tctx.GetDataSourceLocked()->debug_marker_stage(tctx, app_event.c_str()) : + device->tracepoint_iids[tracepoint_idx]; + + auto packet = tctx.NewTracePacket(); + + packet->set_timestamp(start_ns); + packet->set_timestamp_clock_id(device->gpu_clock_id); + + assert(ts_ns >= start_ns); + + auto event = packet->set_gpu_render_stage_event(); + event->set_gpu_id(device->gpu_id); + + event->set_hw_queue_iid(stage->queue_iid); + event->set_stage_iid(stage_iid); + event->set_context(device->iid); + event->set_event_id(evt_id); + event->set_duration(ts_ns - start_ns); + event->set_submission_id(submission_id); + + if ((payload || indirect_data) && payload_as_extra) { + payload_as_extra(event, payload, indirect_data); + } + }); + + stage->start_ns[level] = 0; +} + static size_t snprintf_stages(char *buf, size_t buf_size, enum intel_ds_barrier_type type, @@ -462,6 +531,51 @@ extern "C" { &trace_payload_as_extra_intel_end_##event_name); \ } \ +/* + * Like CREATE_DUAL_EVENT_CALLBACK, but for dynamic event names. + * + * The name is formatted with snprintf() into a stack-local buffer, copied into + * a std::string, and passed by value to end_event_dyn(). That keeps the name + * alive in the Trace() lambda closure and avoids dangling stack pointers. + */ +#define CREATE_DUAL_EVENT_CALLBACK_DYN(event_name, stage, name_fmt, ...) \ + void \ + intel_ds_begin_##event_name(struct intel_ds_device *device, \ + uint64_t ts_ns, \ + uint16_t tp_idx, \ + const void *flush_data, \ + const struct trace_intel_begin_##event_name *payload, \ + const void *indirect_data) \ + { \ + const struct intel_ds_flush_data *flush = \ + (const struct intel_ds_flush_data *) flush_data; \ + begin_event(flush->queue, ts_ns, stage); \ + } \ + \ + void \ + intel_ds_end_##event_name(struct intel_ds_device *device, \ + uint64_t ts_ns, \ + uint16_t tp_idx, \ + const void *flush_data, \ + const struct trace_intel_end_##event_name *payload, \ + const void *indirect_data) \ + { \ + const struct intel_ds_flush_data *flush = \ + (const struct intel_ds_flush_data *) flush_data; \ + UNUSED const uint32_t *indirect = \ + (const uint32_t *) indirect_data; \ + char buf[64]; \ + std::string name; \ + if ((name_fmt) != NULL) { \ + snprintf(buf, sizeof(buf), (name_fmt), ##__VA_ARGS__); \ + name = buf; \ + } \ + end_event_dyn(flush->queue, ts_ns, stage, flush->submission_id, \ + tp_idx, std::move(name), payload, indirect_data, \ + (trace_payload_as_extra_func) \ + &trace_payload_as_extra_intel_end_##event_name); \ + } \ + CREATE_DUAL_EVENT_CALLBACK(frame, INTEL_DS_QUEUE_STAGE_FRAME) CREATE_DUAL_EVENT_CALLBACK(batch, INTEL_DS_QUEUE_STAGE_CMD_BUFFER) CREATE_DUAL_EVENT_CALLBACK(cmd_buffer, INTEL_DS_QUEUE_STAGE_CMD_BUFFER)