From a1ac3c2e8dfd704f63e6066146a7cccd3f789fb0 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 6 Apr 2026 14:16:44 +0300 Subject: [PATCH] timeline: Add support for weston_input_event And with it include a dedicated track in Perfetto to display the event timestamp on that a weston_seat. This includes support for timeline and for timeline Perfetto. Signed-off-by: Marius Vlad --- include/libweston/libweston.h | 2 + libweston/input.c | 4 ++ libweston/perfetto/u_perfetto.cc | 26 +++++++++++++ libweston/perfetto/u_perfetto.h | 9 +++++ libweston/timeline-perfetto.c | 32 +++++++++++++++- libweston/timeline.c | 66 ++++++++++++++++++++++++++++++++ libweston/timeline.h | 5 ++- libweston/weston-trace.h | 19 +++++++++ 8 files changed, 161 insertions(+), 2 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 22156d338..80db69fa1 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -609,6 +609,7 @@ enum weston_pointer_motion_mask { struct weston_input_event { struct timespec ts; struct weston_seat *seat; + uint64_t flow_id; }; struct weston_pointer_motion_event { @@ -1247,6 +1248,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 { diff --git a/libweston/input.c b/libweston/input.c index 1e24554e6..2093fa834 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -5972,6 +5972,10 @@ weston_input_event_init(struct weston_input_event *ievent, struct timespec *ts, { ievent->ts = *ts; ievent->seat = seat; + ievent->flow_id = 0; + + WESTON_TRACE_FUNC_FLOW(&ievent->flow_id); + } WL_EXPORT void diff --git a/libweston/perfetto/u_perfetto.cc b/libweston/perfetto/u_perfetto.cc index f99240a56..dbf163c9e 100644 --- a/libweston/perfetto/u_perfetto.cc +++ b/libweston/perfetto/u_perfetto.cc @@ -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) { diff --git a/libweston/perfetto/u_perfetto.h b/libweston/perfetto/u_perfetto.h index febbefc0d..131d0e889 100644 --- a/libweston/perfetto/u_perfetto.h +++ b/libweston/perfetto/u_perfetto.h @@ -96,6 +96,9 @@ void util_perfetto_trace_commit_annotate_func(const char *name, void util_perfetto_trace_commit_annotate_func_flow(uint64_t id, const char *name, struct weston_debug_annotations *annots); +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); @@ -155,6 +158,12 @@ util_perfetto_trace_commit_annotate_func_flow(uint64_t id, const char *name, { } +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) { } diff --git a/libweston/timeline-perfetto.c b/libweston/timeline-perfetto.c index 54dfbb341..58787e860 100644 --- a/libweston/timeline-perfetto.c +++ b/libweston/timeline-perfetto.c @@ -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,10 @@ weston_timeline_perfetto(struct weston_log_scope *timeline_scope, { struct weston_output *output = NULL; struct weston_surface *surface = 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 +152,11 @@ weston_timeline_perfetto(struct weston_log_scope *timeline_scope, case TLT_GPU: gpu_ns = 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 +197,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", ievent->seat->track_id, ievent->flow_id, CLOCK_MONOTONIC, kernel_input_ts); + break; default: assert(!"not reached"); } diff --git a/libweston/timeline.c b/libweston/timeline.c index f919ebc3b..38c90f552 100644 --- a/libweston/timeline.c +++ b/libweston/timeline.c @@ -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,36 @@ 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 struct weston_timeline_subscription_object * check_weston_surface_description(struct weston_log_subscription *sub, @@ -342,6 +390,21 @@ emit_gpu_timestamp(struct timeline_emit_context *ctx, void *obj) 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 +455,7 @@ static const type_func type_dispatch[] = { [TLT_SURFACE] = emit_weston_surface, [TLT_VBLANK] = emit_vblank_timestamp, [TLT_GPU] = emit_gpu_timestamp, + [TLT_INPUT_EVENT] = emit_input_event, }; static const char * @@ -420,6 +484,8 @@ tlp_to_string(enum timeline_point_name tlp) return "renderer_gpu_begin"; case TLP_RENDERER_GPU_END: return "renderer_gpu_end"; + case TLP_INPUT_KERNEL_TS: + return "event ts"; } assert(!"not reached"); } diff --git a/libweston/timeline.h b/libweston/timeline.h index 95b65b52c..8c0688e50 100644 --- a/libweston/timeline.h +++ b/libweston/timeline.h @@ -43,6 +43,7 @@ enum timeline_type { TLT_SURFACE, TLT_VBLANK, TLT_GPU, + TLT_INPUT_EVENT, }; enum timeline_point_name { @@ -56,7 +57,8 @@ 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_KERNEL_TS, }; /** Timeline subscription created for each subscription @@ -96,6 +98,7 @@ 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_INPUT_EVENT(t) TLT_INPUT_EVENT, TYPEVERIFY(const struct weston_input_event *, (t)) /** This macro is used to add timeline points. * diff --git a/libweston/weston-trace.h b/libweston/weston-trace.h index 8354e7cbb..322dc1b30 100644 --- a/libweston/weston-trace.h +++ b/libweston/weston-trace.h @@ -152,6 +152,12 @@ #define _WESTON_TRACE_ANNOTATE_PAIR(pair) _WESTON_TRACE_ANNOTATE_ADD_GENERIC pair /* end of helper section */ +#define _WESTON_TRACE_INSTANT_TIMESTAMP(name, track_id, id, clock, timestamp) \ + do { \ + if (unlikely(util_perfetto_is_tracing_enabled())) \ + _weston_trace_instant_timestamp(name, track_id, id, \ + clock, timestamp); \ + } while (0) #if __has_attribute(cleanup) && __has_attribute(unused) @@ -255,6 +261,15 @@ _weston_trace_annotate_func_begin_flow(const char *name, uint64_t *id, return 0; } +static inline int +_weston_trace_instant_timestamp(const char *name, uint64_t track_id, uint64_t id, + clock_t clock, uint64_t ts) +{ + weston_assert_u64_gt(NULL, id, 0); + util_perfetto_trace_instant_timestamp(name, track_id, id, clock, ts); + return 0; +} + static inline void _weston_trace_scope_end(int *scope) { @@ -276,6 +291,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) #define _WESTON_TRACE_BEGIN_ANNOTATION() #define _WESTON_TRACE_COMMIT_ANNOTATION(id, name) @@ -294,6 +310,9 @@ _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) + #define WESTON_TRACE_BEGIN_ANNOTATION() \ _WESTON_TRACE_BEGIN_ANNOTATION()