blob: d5738f48c64e6c93aeebb64a945bd814f5610740 [file] [log] [blame]
Pekka Paalanenb5026542014-11-12 15:09:24 +02001/*
2 * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
Marius Vlad2a1b7862019-09-05 13:12:18 +03003 * Copyright © 2014, 2019 Collabora, Ltd.
Pekka Paalanenb5026542014-11-12 15:09:24 +02004 *
Bryce Harringtona0bbfea2015-06-11 15:35:43 -07005 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
Pekka Paalanenb5026542014-11-12 15:09:24 +020012 *
Bryce Harringtona0bbfea2015-06-11 15:35:43 -070013 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
Pekka Paalanenb5026542014-11-12 15:09:24 +020025 */
26
27#include "config.h"
28
29#include <stdio.h>
30#include <errno.h>
31#include <string.h>
32#include <time.h>
33#include <assert.h>
34
Pekka Paalanen3d5d9472019-03-28 16:28:47 +020035#include <libweston/libweston.h>
Marius Vladfb10ed72019-09-05 14:20:43 +030036#include <libweston/weston-log.h>
37#include "timeline.h"
38#include "weston-log-internal.h"
Pekka Paalanenb5026542014-11-12 15:09:24 +020039
Marius Vlad23d01c62019-09-05 17:56:12 +030040/**
41 * Timeline itself is not a subscriber but a scope (a producer of data), and it
42 * re-routes the data it produces to all the subscriptions (and implicitly
43 * to the subscribers) using a subscription iteration to go through all of them.
44 *
45 * Public API:
46 * * weston_timeline_refresh_subscription_objects() - allows outside parts of
47 * libweston notify/signal timeline code about the fact that underlying object
48 * has suffered some modifications and needs to re-emit the object ID.
49 * * weston_log_timeline_point() - which will disseminate data to all
50 * subscriptions
51 *
52 * Do note that only weston_timeline_refresh_subscription_objects()
53 * is exported in libweston.
54 *
55 * Destruction of the objects assigned to each underlying objects happens in
56 * two places: one in the logging framework callback of the log scope
57 * ('destroy_subscription'), and secondly, when the object itself gets
58 * destroyed.
59 *
60 * timeline_emit_context - For each subscription this object will be created to
61 * store a buffer when the object itself will be written and a subscription,
62 * which will be used to force the object ID if there is a need to do so (the
63 * underlying object has been refreshed, or better said has suffered some
64 * modification). Data written to a subscription will be flushed before the
65 * data written to the FILE *.
66 *
67 * @param cur a FILE *
68 * @param subscription a pointer to an already created subscription
69 *
70 * @ingroup internal-log
71 * @sa weston_timeline_point
72 */
Pekka Paalanenb5026542014-11-12 15:09:24 +020073struct timeline_emit_context {
74 FILE *cur;
Marius Vladfb10ed72019-09-05 14:20:43 +030075 struct weston_log_subscription *subscription;
Pekka Paalanenb5026542014-11-12 15:09:24 +020076};
77
Marius Vladfb10ed72019-09-05 14:20:43 +030078/** Create a timeline subscription and hang it off the subscription
79 *
80 * Called when the subscription is created.
81 *
82 * @ingroup internal-log
83 */
84void
85weston_timeline_create_subscription(struct weston_log_subscription *sub,
86 void *user_data)
Pekka Paalanenb5026542014-11-12 15:09:24 +020087{
Marius Vladfb10ed72019-09-05 14:20:43 +030088 struct weston_timeline_subscription *tl_sub = zalloc(sizeof(*tl_sub));
89 if (!tl_sub)
90 return;
Pekka Paalanenb5026542014-11-12 15:09:24 +020091
Marius Vladfb10ed72019-09-05 14:20:43 +030092 wl_list_init(&tl_sub->objects);
Pekka Paalanenb5026542014-11-12 15:09:24 +020093
Marius Vladfb10ed72019-09-05 14:20:43 +030094 /* attach this timeline_subscription to it */
95 weston_log_subscription_set_data(sub, tl_sub);
Pekka Paalanenb5026542014-11-12 15:09:24 +020096}
97
98static void
Marius Vladfb10ed72019-09-05 14:20:43 +030099weston_timeline_destroy_subscription_object(struct weston_timeline_subscription_object *sub_obj)
100{
101 /* remove the notify listener */
102 wl_list_remove(&sub_obj->destroy_listener.link);
103 sub_obj->destroy_listener.notify = NULL;
104
105 wl_list_remove(&sub_obj->subscription_link);
106 free(sub_obj);
107}
108
109/** Destroy the timeline subscription and all timeline subscription objects
110 * associated with it.
111 *
112 * Called when (before) the subscription is destroyed.
113 *
114 * @ingroup internal-log
115 */
116void
117weston_timeline_destroy_subscription(struct weston_log_subscription *sub,
118 void *user_data)
119{
120 struct weston_timeline_subscription *tl_sub =
121 weston_log_subscription_get_data(sub);
122 struct weston_timeline_subscription_object *sub_obj, *tmp_sub_obj;
123
124 if (!tl_sub)
125 return;
126
127 wl_list_for_each_safe(sub_obj, tmp_sub_obj,
128 &tl_sub->objects, subscription_link)
129 weston_timeline_destroy_subscription_object(sub_obj);
130
131 free(tl_sub);
132}
133
134static bool
135weston_timeline_check_object_refresh(struct weston_timeline_subscription_object *obj)
136{
137 if (obj->force_refresh == true) {
138 obj->force_refresh = false;
139 return true;
140 }
141 return false;
142}
143
144static struct weston_timeline_subscription_object *
145weston_timeline_subscription_search(struct weston_timeline_subscription *tl_sub,
146 void *object)
147{
148 struct weston_timeline_subscription_object *sub_obj;
149
150 wl_list_for_each(sub_obj, &tl_sub->objects, subscription_link)
151 if (sub_obj->object == object)
152 return sub_obj;
153
154 return NULL;
155}
156
157static struct weston_timeline_subscription_object *
158weston_timeline_subscription_object_create(void *object,
159 struct weston_timeline_subscription *tm_sub)
160{
161 struct weston_timeline_subscription_object *sub_obj;
162
163 sub_obj = zalloc(sizeof(*sub_obj));
164 sub_obj->id = ++tm_sub->next_id;
165 sub_obj->object = object;
166
167 /* when the object is created so that it has the chance to display the
168 * object ID, we set the refresh status; it will only be re-freshed by
169 * the backend (or part parts) when the underlying objects has suffered
170 * modifications */
171 sub_obj->force_refresh = true;
172
173 wl_list_insert(&tm_sub->objects, &sub_obj->subscription_link);
174
175 return sub_obj;
176}
177
178static void
179weston_timeline_destroy_subscription_object_notify(struct wl_listener *listener, void *data)
180{
181 struct weston_timeline_subscription_object *sub_obj;
182
183 sub_obj = wl_container_of(listener, sub_obj, destroy_listener);
184 weston_timeline_destroy_subscription_object(sub_obj);
185}
186
187static struct weston_timeline_subscription_object *
188weston_timeline_subscription_output_ensure(struct weston_timeline_subscription *tl_sub,
189 struct weston_output *output)
190{
191 struct weston_timeline_subscription_object *sub_obj;
192
193 sub_obj = weston_timeline_subscription_search(tl_sub, output);
194 if (!sub_obj) {
195 sub_obj = weston_timeline_subscription_object_create(output, tl_sub);
196
197 sub_obj->destroy_listener.notify =
198 weston_timeline_destroy_subscription_object_notify;
199 wl_signal_add(&output->destroy_signal,
200 &sub_obj->destroy_listener);
201 }
202 return sub_obj;
203}
204
205static struct weston_timeline_subscription_object *
206weston_timeline_subscription_surface_ensure(struct weston_timeline_subscription *tl_sub,
207 struct weston_surface *surface)
208{
209 struct weston_timeline_subscription_object *sub_obj;
210
211 sub_obj = weston_timeline_subscription_search(tl_sub, surface);
212 if (!sub_obj) {
213 sub_obj = weston_timeline_subscription_object_create(surface, tl_sub);
214
215 sub_obj->destroy_listener.notify =
216 weston_timeline_destroy_subscription_object_notify;
217 wl_signal_add(&surface->destroy_signal,
218 &sub_obj->destroy_listener);
219 }
220
221 return sub_obj;
222}
223
224static void
225fprint_quoted_string(struct weston_log_subscription *sub, const char *str)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200226{
227 if (!str) {
Marius Vladfb10ed72019-09-05 14:20:43 +0300228 weston_log_subscription_printf(sub, "null");
Pekka Paalanenb5026542014-11-12 15:09:24 +0200229 return;
230 }
231
Marius Vladfb10ed72019-09-05 14:20:43 +0300232 weston_log_subscription_printf(sub, "\"%s\"", str);
233}
234
235static void
236emit_weston_output_print_id(struct weston_log_subscription *sub,
237 struct weston_timeline_subscription_object *sub_obj,
238 const char *name)
239{
240 if (!weston_timeline_check_object_refresh(sub_obj))
241 return;
242
243 weston_log_subscription_printf(sub, "{ \"id\":%u, "
244 "\"type\":\"weston_output\", \"name\":", sub_obj->id);
245 fprint_quoted_string(sub, name);
246 weston_log_subscription_printf(sub, " }\n");
Pekka Paalanenb5026542014-11-12 15:09:24 +0200247}
248
249static int
250emit_weston_output(struct timeline_emit_context *ctx, void *obj)
251{
Marius Vladfb10ed72019-09-05 14:20:43 +0300252 struct weston_log_subscription *sub = ctx->subscription;
253 struct weston_output *output = obj;
254 struct weston_timeline_subscription_object *sub_obj;
255 struct weston_timeline_subscription *tl_sub;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200256
Marius Vladfb10ed72019-09-05 14:20:43 +0300257 tl_sub = weston_log_subscription_get_data(sub);
258 sub_obj = weston_timeline_subscription_output_ensure(tl_sub, output);
259 emit_weston_output_print_id(sub, sub_obj, output->name);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200260
Marius Vladfb10ed72019-09-05 14:20:43 +0300261 assert(sub_obj->id != 0);
262 fprintf(ctx->cur, "\"wo\":%u", sub_obj->id);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200263
264 return 1;
265}
266
Marius Vladfb10ed72019-09-05 14:20:43 +0300267
Pekka Paalanenb5026542014-11-12 15:09:24 +0200268static void
Marius Vladfb10ed72019-09-05 14:20:43 +0300269check_weston_surface_description(struct weston_log_subscription *sub,
270 struct weston_surface *s,
271 struct weston_timeline_subscription *tm_sub,
272 struct weston_timeline_subscription_object *sub_obj)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200273{
274 struct weston_surface *mains;
275 char d[512];
276 char mainstr[32];
277
Marius Vladfb10ed72019-09-05 14:20:43 +0300278 if (!weston_timeline_check_object_refresh(sub_obj))
Pekka Paalanenb5026542014-11-12 15:09:24 +0200279 return;
280
281 mains = weston_surface_get_main_surface(s);
282 if (mains != s) {
Marius Vladfb10ed72019-09-05 14:20:43 +0300283 struct weston_timeline_subscription_object *new_sub_obj;
284
285 new_sub_obj = weston_timeline_subscription_surface_ensure(tm_sub, mains);
286 check_weston_surface_description(sub, mains, tm_sub, new_sub_obj);
287 if (snprintf(mainstr, sizeof(mainstr), ", \"main_surface\":%u",
288 new_sub_obj->id) < 0)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200289 mainstr[0] = '\0';
290 } else {
291 mainstr[0] = '\0';
292 }
293
294 if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0)
295 d[0] = '\0';
296
Marius Vladfb10ed72019-09-05 14:20:43 +0300297 weston_log_subscription_printf(sub, "{ \"id\":%u, "
298 "\"type\":\"weston_surface\", \"desc\":",
299 sub_obj->id);
300 fprint_quoted_string(sub, d[0] ? d : NULL);
301 weston_log_subscription_printf(sub, "%s }\n", mainstr);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200302}
303
304static int
305emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
306{
Marius Vladfb10ed72019-09-05 14:20:43 +0300307 struct weston_log_subscription *sub = ctx->subscription;
308 struct weston_surface *surface = obj;
309 struct weston_timeline_subscription_object *sub_obj;
310 struct weston_timeline_subscription *tl_sub;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200311
Marius Vladfb10ed72019-09-05 14:20:43 +0300312 tl_sub = weston_log_subscription_get_data(sub);
313 sub_obj = weston_timeline_subscription_surface_ensure(tl_sub, surface);
314 check_weston_surface_description(sub, surface, tl_sub, sub_obj);
315
316 assert(sub_obj->id != 0);
317 fprintf(ctx->cur, "\"ws\":%u", sub_obj->id);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200318
319 return 1;
320}
321
322static int
323emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
324{
325 struct timespec *ts = obj;
326
Pekka Paalanen50aa3a72020-05-28 11:34:04 +0300327 fprintf(ctx->cur, "\"vblank_monotonic\":[%" PRId64 ", %ld]",
Pekka Paalanenb5026542014-11-12 15:09:24 +0200328 (int64_t)ts->tv_sec, ts->tv_nsec);
329
330 return 1;
331}
332
Alexandros Frantzis75d38ef2017-09-27 15:09:13 +0300333static int
334emit_gpu_timestamp(struct timeline_emit_context *ctx, void *obj)
335{
336 struct timespec *ts = obj;
337
338 fprintf(ctx->cur, "\"gpu\":[%" PRId64 ", %ld]",
339 (int64_t)ts->tv_sec, ts->tv_nsec);
340
341 return 1;
342}
343
Marius Vlad5de92972019-10-15 13:25:41 +0300344static struct weston_timeline_subscription_object *
345weston_timeline_get_subscription_object(struct weston_log_subscription *sub,
346 void *object)
347{
348 struct weston_timeline_subscription *tl_sub;
349
350 tl_sub = weston_log_subscription_get_data(sub);
351 if (!tl_sub)
352 return NULL;
353
354 return weston_timeline_subscription_search(tl_sub, object);
355}
356
Marius Vlad23d01c62019-09-05 17:56:12 +0300357/** Sets (on) the timeline subscription object refresh status.
358 *
359 * This function 'notifies' timeline to print the object ID. The timeline code
360 * will reset it back, so there's no need for users to do anything about it.
361 *
362 * Can be used from outside libweston.
363 *
364 * @param wc a weston_compositor instance
365 * @param object the underyling object
366 *
367 * @ingroup log
368 */
Marius Vlad5de92972019-10-15 13:25:41 +0300369WL_EXPORT void
370weston_timeline_refresh_subscription_objects(struct weston_compositor *wc,
371 void *object)
372{
373 struct weston_log_subscription *sub = NULL;
374
375 while ((sub = weston_log_subscription_iterate(wc->timeline, sub))) {
376 struct weston_timeline_subscription_object *sub_obj;
377
378 sub_obj = weston_timeline_get_subscription_object(sub, object);
379 if (sub_obj)
380 sub_obj->force_refresh = true;
381 }
382}
383
Pekka Paalanenb5026542014-11-12 15:09:24 +0200384typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
385
386static const type_func type_dispatch[] = {
387 [TLT_OUTPUT] = emit_weston_output,
388 [TLT_SURFACE] = emit_weston_surface,
389 [TLT_VBLANK] = emit_vblank_timestamp,
Alexandros Frantzis75d38ef2017-09-27 15:09:13 +0300390 [TLT_GPU] = emit_gpu_timestamp,
Pekka Paalanenb5026542014-11-12 15:09:24 +0200391};
392
Marius Vlad23d01c62019-09-05 17:56:12 +0300393/** Disseminates the message to all subscriptions of the scope \c
394 * timeline_scope
395 *
396 * The TL_POINT() is a wrapper over this function, but it uses the weston_compositor
397 * instance to pass the timeline scope.
398 *
399 * @param timeline_scope the timeline scope
400 * @param name the name of the timeline point. Interpretable by the tool reading
401 * the output (wesgr).
402 *
403 * @ingroup log
404 */
Pekka Paalanenb5026542014-11-12 15:09:24 +0200405WL_EXPORT void
Marius Vlad3203ff62019-09-05 14:56:12 +0300406weston_timeline_point(struct weston_log_scope *timeline_scope,
407 const char *name, ...)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200408{
Pekka Paalanenb5026542014-11-12 15:09:24 +0200409 struct timespec ts;
410 enum timeline_type otype;
411 void *obj;
412 char buf[512];
Marius Vlad3203ff62019-09-05 14:56:12 +0300413 struct weston_log_subscription *sub = NULL;
414
415 if (!weston_log_scope_is_enabled(timeline_scope))
416 return;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200417
Marius Vlad2a1b7862019-09-05 13:12:18 +0300418 clock_gettime(CLOCK_MONOTONIC, &ts);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200419
Marius Vlad3203ff62019-09-05 14:56:12 +0300420 while ((sub = weston_log_subscription_iterate(timeline_scope, sub))) {
421 va_list argp;
422 struct timeline_emit_context ctx = {};
Pekka Paalanenb5026542014-11-12 15:09:24 +0200423
Marius Vlad3203ff62019-09-05 14:56:12 +0300424 memset(buf, 0, sizeof(buf));
425 ctx.cur = fmemopen(buf, sizeof(buf), "w");
426 ctx.subscription = sub;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200427
Marius Vlad3203ff62019-09-05 14:56:12 +0300428 if (!ctx.cur) {
429 weston_log("Timeline error in fmemopen, closing.\n");
430 return;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200431 }
Pekka Paalanenb5026542014-11-12 15:09:24 +0200432
Marius Vlad3203ff62019-09-05 14:56:12 +0300433 fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
434 (int64_t)ts.tv_sec, ts.tv_nsec, name);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200435
Marius Vlad3203ff62019-09-05 14:56:12 +0300436 va_start(argp, name);
437 while (1) {
438 otype = va_arg(argp, enum timeline_type);
439 if (otype == TLT_END)
440 break;
441
442 obj = va_arg(argp, void *);
443 if (type_dispatch[otype]) {
444 fprintf(ctx.cur, ", ");
445 type_dispatch[otype](&ctx, obj);
446 }
447 }
448 va_end(argp);
449
450 fprintf(ctx.cur, " }\n");
451 fflush(ctx.cur);
452 if (ferror(ctx.cur)) {
453 weston_log("Timeline error in constructing entry, closing.\n");
454 } else {
455 weston_log_subscription_printf(ctx.subscription, "%s", buf);
456 }
457
458 fclose(ctx.cur);
459
460 }
Pekka Paalanenb5026542014-11-12 15:09:24 +0200461}