timeline: Add support for weston_seat

And with it include a dedicated track in Perfetto to display event time
on a particular seat.

Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
This commit is contained in:
Marius Vlad 2026-04-06 14:16:44 +03:00
parent 7993052a1f
commit 3f727bf1b5
7 changed files with 187 additions and 2 deletions

View file

@ -1243,6 +1243,7 @@ struct weston_seat {
struct wl_list tablet_tool_list;
struct wl_list tablet_seat_resource_list;
struct wl_signal tablet_tool_added_signal;
uint64_t track_id;
};
enum {

View file

@ -145,6 +145,32 @@ util_perfetto_counter_set(const char *name, double value)
perfetto::DynamicString(name), value);
}
void
util_perfetto_trace_instant_timestamp(const char *name, uint64_t track_id, uint64_t id, clockid_t clock, uint64_t ts)
{
if (id) {
TRACE_EVENT_INSTANT(UTIL_PERFETTO_CATEGORY_DEFAULT_STR,
nullptr,
perfetto::Track(track_id),
perfetto::TraceTimestamp{clockid_to_perfetto_clock(clock), ts},
perfetto::Flow::ProcessScoped(id),
[&](perfetto::EventContext ctx) {
ctx.event()->set_name(name);
ctx.AddDebugAnnotation(name, ts);
});
return;
}
TRACE_EVENT_INSTANT(UTIL_PERFETTO_CATEGORY_DEFAULT_STR,
nullptr,
perfetto::Track(track_id),
perfetto::TraceTimestamp{clockid_to_perfetto_clock(clock), ts},
[&](perfetto::EventContext ctx) {
ctx.event()->set_name(name);
ctx.AddDebugAnnotation(name, ts);
});
}
uint64_t
util_perfetto_next_id(void)
{

View file

@ -63,10 +63,13 @@ void util_perfetto_trace_full_begin(const char *name, uint64_t track_id, uint64_
void util_perfetto_trace_full_end(const char *name, uint64_t track_id, clockid_t clock, uint64_t timestamp);
void util_perfetto_trace_instant_timestamp(const char *name, uint64_t track_id, uint64_t id, clockid_t clock, uint64_t ts);
uint64_t util_perfetto_next_id(void);
uint64_t util_perfetto_new_track(const char *name);
#else /* HAVE_PERFETTO */
static inline void
@ -104,6 +107,11 @@ util_perfetto_trace_full_end(const char *name, uint64_t track_id, clockid_t cloc
{
}
static inline void
util_perfetto_trace_instant_timestamp(const char *name, uint64_t track_id, clockid_t clock, uint64_t ts)
{
}
static inline void util_perfetto_counter_set(const char *name, double value)
{
}

View file

@ -64,6 +64,14 @@ build_track_name(struct weston_surface *surface, char *name, int size)
snprintf(name, size, "%s #%d", surface->label, surface->s_id);
}
static void
build_seat_track_name(struct weston_seat *seat, char *name, int size)
{
assert(seat->track_id == 0);
snprintf(name, size, "seat: %s", seat->seat_name);
}
static void
weston_perfetto_ensure_surface_id(struct weston_surface *surface)
{
@ -77,6 +85,19 @@ weston_perfetto_ensure_surface_id(struct weston_surface *surface)
surface->damage_track_id = util_perfetto_new_track(track_name);
}
static void
weston_perfetto_ensure_seat_id(struct weston_seat *seat)
{
char track_name[600];
if (seat->track_id)
return;
build_seat_track_name(seat, track_name, sizeof(track_name));
seat->track_id = util_perfetto_new_track(track_name);
}
/**
* Translates a timeline point for perfetto.
*
@ -94,9 +115,11 @@ weston_timeline_perfetto(struct weston_log_scope *timeline_scope,
{
struct weston_output *output = NULL;
struct weston_surface *surface = NULL;
struct weston_seat *seat = NULL;
struct weston_input_event *ievent = NULL;
struct timespec ts;
uint64_t now_ns;
uint64_t vblank_ns = 0, gpu_ns = 0;
uint64_t vblank_ns = 0, gpu_ns = 0, kernel_input_ts = 0;
va_list argp;
if (!util_perfetto_is_tracing_enabled())
@ -130,6 +153,18 @@ weston_timeline_perfetto(struct weston_log_scope *timeline_scope,
case TLT_GPU:
gpu_ns = timespec_to_nsec(obj);
break;
case TLT_SEAT:
seat = obj;
weston_perfetto_ensure_seat_id(seat);
break;
case TLT_KERNEL_TS:
kernel_input_ts = timespec_to_nsec(obj);
break;
case TLT_INPUT_EVENT:
ievent = obj;
weston_perfetto_ensure_seat_id(ievent->seat);
kernel_input_ts = timespec_to_nsec(ievent->ts);
break;
default:
assert(!"not reached");
}
@ -170,6 +205,9 @@ weston_timeline_perfetto(struct weston_log_scope *timeline_scope,
case TLP_RENDERER_GPU_END:
WESTON_TRACE_TIMESTAMP_END("Active", output->gpu_track_id, CLOCK_MONOTONIC, gpu_ns);
break;
case TLP_INPUT_KERNEL_TS:
WESTON_TRACE_INSTANT_TIMESTAMP("event ts", seat->track_id, 0, CLOCK_MONOTONIC, kernel_input_ts);
break;
default:
assert(!"not reached");
}

View file

@ -222,6 +222,24 @@ weston_timeline_subscription_surface_ensure(struct weston_timeline_subscription
return sub_obj;
}
static struct weston_timeline_subscription_object *
weston_timeline_subscription_seat_ensure(struct weston_timeline_subscription *tl_sub,
struct weston_seat *seat)
{
struct weston_timeline_subscription_object *sub_obj;
sub_obj = weston_timeline_subscription_search(tl_sub, seat);
if (!sub_obj) {
sub_obj = weston_timeline_subscription_object_create(seat, tl_sub);
sub_obj->destroy_listener.notify =
weston_timeline_destroy_subscription_object_notify;
wl_signal_add(&seat->destroy_signal,
&sub_obj->destroy_listener);
}
return sub_obj;
}
static void
fprint_quoted_string(struct weston_log_subscription *sub, const char *str)
{
@ -265,6 +283,47 @@ emit_weston_output(struct timeline_emit_context *ctx, void *obj)
return 1;
}
static void
emit_weston_seat_print_id(struct weston_log_subscription *sub,
struct weston_timeline_subscription_object *sub_obj,
const char *name)
{
if (!weston_timeline_check_object_refresh(sub_obj))
return;
weston_log_subscription_printf(sub, "{ \"id\":%u, "
"\"type\":\"weston_seat\", \"name\":", sub_obj->id);
fprint_quoted_string(sub, name);
weston_log_subscription_printf(sub, " }\n");
}
static int
common_emit_seat(struct timeline_emit_context *ctx, struct weston_seat *seat)
{
struct weston_log_subscription *sub = ctx->subscription;
struct weston_timeline_subscription_object *sub_obj;
struct weston_timeline_subscription *tl_sub;
tl_sub = weston_log_subscription_get_data(sub);
sub_obj = weston_timeline_subscription_seat_ensure(tl_sub, seat);
emit_weston_seat_print_id(sub, sub_obj, seat->seat_name);
assert(sub_obj->id != 0);
fprintf(ctx->cur, "\"seat\":%u", sub_obj->id);
return 1;
}
static int
emit_seat(struct timeline_emit_context *ctx, void *obj)
{
struct weston_seat *seat = obj;
common_emit_seat(ctx, seat);
return 1;
}
static struct weston_timeline_subscription_object *
check_weston_surface_description(struct weston_log_subscription *sub,
@ -342,6 +401,32 @@ emit_gpu_timestamp(struct timeline_emit_context *ctx, void *obj)
return 1;
}
static int
emit_kernel_input_timestamp(struct timeline_emit_context *ctx, void *obj)
{
struct timespec *ts = obj;
fprintf(ctx->cur, "\"event ts\":[%" PRId64 ", %ld]",
(int64_t)ts->tv_sec, ts->tv_nsec);
return 1;
}
static int
emit_input_event(struct timeline_emit_context *ctx, void *obj)
{
struct weston_input_event *ievent = obj;
struct timespec *ts = ievent->ts;
struct weston_seat *seat = ievent->seat;
common_emit_seat(ctx, seat);
fprintf(ctx->cur, ", \"event ts\":[%" PRId64 ", %ld]",
(int64_t)ts->tv_sec, ts->tv_nsec);
return 1;
}
static struct weston_timeline_subscription_object *
weston_timeline_get_subscription_object(struct weston_log_subscription *sub,
void *object)
@ -392,6 +477,9 @@ static const type_func type_dispatch[] = {
[TLT_SURFACE] = emit_weston_surface,
[TLT_VBLANK] = emit_vblank_timestamp,
[TLT_GPU] = emit_gpu_timestamp,
[TLT_SEAT] = emit_seat,
[TLT_KERNEL_TS] = emit_kernel_input_timestamp,
[TLT_INPUT_EVENT] = emit_input_event,
};
static const char *
@ -420,6 +508,10 @@ tlp_to_string(enum timeline_point_name tlp)
return "renderer_gpu_begin";
case TLP_RENDERER_GPU_END:
return "renderer_gpu_end";
case TLP_INPUT_SEAT:
return "seat";
case TLP_INPUT_KERNEL_TS:
return "kernel_ts";
}
assert(!"not reached");
}

View file

@ -43,6 +43,9 @@ enum timeline_type {
TLT_SURFACE,
TLT_VBLANK,
TLT_GPU,
TLT_SEAT,
TLT_KERNEL_TS,
TLT_INPUT_EVENT,
};
enum timeline_point_name {
@ -56,7 +59,9 @@ enum timeline_point_name {
TLP_CORE_REPAINT_ENTER_LOOP,
TLP_CORE_COMMIT_DAMAGE,
TLP_RENDERER_GPU_BEGIN,
TLP_RENDERER_GPU_END
TLP_RENDERER_GPU_END,
TLP_INPUT_SEAT,
TLP_INPUT_KERNEL_TS,
};
/** Timeline subscription created for each subscription
@ -96,6 +101,9 @@ struct weston_timeline_subscription_object {
#define TLP_SURFACE(s) TLT_SURFACE, TYPEVERIFY(struct weston_surface *, (s))
#define TLP_VBLANK(t) TLT_VBLANK, TYPEVERIFY(const struct timespec *, (t))
#define TLP_GPU(t) TLT_GPU, TYPEVERIFY(const struct timespec *, (t))
#define TLP_SEAT(t) TLT_SEAT, TYPEVERIFY(const struct weston_seat *, (t))
#define TLP_KERNEL_TS(t) TLT_KERNEL_TS, TYPEVERIFY(const struct timespec *, (t))
#define TLP_INPUT_EVENT(t) TLT_INPUT_EVENT, TYPEVERIFY(const struct weston_input_event *, (t))
/** This macro is used to add timeline points.
*

View file

@ -69,6 +69,14 @@
clock, timestamp); \
} while (0)
#define _WESTON_TRACE_INSTANT_TIMESTAMP(name, track_id, id, clock, timestamp) \
do { \
if (unlikely(util_perfetto_is_tracing_enabled())) \
util_perfetto_trace_instant_timestamp(name, track_id, \
id, clock, \
timestamp); \
} while (0)
#if __has_attribute(cleanup) && __has_attribute(unused)
#define _WESTON_TRACE_SCOPE_VAR_CONCAT(name, suffix) name##suffix
@ -129,6 +137,7 @@ _weston_trace_scope_end(int *scope)
#define _WESTON_TRACE_SET_COUNTER(name, value)
#define _WESTON_TRACE_TIMESTAMP_BEGIN(name, track_id, flow_id, clock, timestamp)
#define _WESTON_TRACE_TIMESTAMP_END(name, track_id, clock, timestamp)
#define _WESTON_TRACE_INSTANT_TIMESTAMP(name, track_id, id, clock, timestamp)
#endif /* HAVE_PERFETTO */
@ -141,5 +150,8 @@ _weston_trace_scope_end(int *scope)
_WESTON_TRACE_TIMESTAMP_BEGIN(name, track_id, flow_id, clock, timestamp)
#define WESTON_TRACE_TIMESTAMP_END(name, track_id, clock, timestamp) \
_WESTON_TRACE_TIMESTAMP_END(name, track_id, clock, timestamp)
#define WESTON_TRACE_INSTANT_TIMESTAMP(name, track_id, id, clock, timestamp) \
_WESTON_TRACE_INSTANT_TIMESTAMP(name, track_id, id, clock, timestamp)
#endif /* WESTON_TRACE_H */