blob: 2c782deb2bb4d8bfe00977a5facd8d6cb8251f2d [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
Pekka Paalanenb5026542014-11-12 15:09:24 +020040struct timeline_emit_context {
41 FILE *cur;
Marius Vladfb10ed72019-09-05 14:20:43 +030042 struct weston_log_subscription *subscription;
Pekka Paalanenb5026542014-11-12 15:09:24 +020043};
44
Marius Vladfb10ed72019-09-05 14:20:43 +030045/** Create a timeline subscription and hang it off the subscription
46 *
47 * Called when the subscription is created.
48 *
49 * @ingroup internal-log
50 */
51void
52weston_timeline_create_subscription(struct weston_log_subscription *sub,
53 void *user_data)
Pekka Paalanenb5026542014-11-12 15:09:24 +020054{
Marius Vladfb10ed72019-09-05 14:20:43 +030055 struct weston_timeline_subscription *tl_sub = zalloc(sizeof(*tl_sub));
56 if (!tl_sub)
57 return;
Pekka Paalanenb5026542014-11-12 15:09:24 +020058
Marius Vladfb10ed72019-09-05 14:20:43 +030059 wl_list_init(&tl_sub->objects);
Pekka Paalanenb5026542014-11-12 15:09:24 +020060
Marius Vladfb10ed72019-09-05 14:20:43 +030061 /* attach this timeline_subscription to it */
62 weston_log_subscription_set_data(sub, tl_sub);
Pekka Paalanenb5026542014-11-12 15:09:24 +020063}
64
65static void
Marius Vladfb10ed72019-09-05 14:20:43 +030066weston_timeline_destroy_subscription_object(struct weston_timeline_subscription_object *sub_obj)
67{
68 /* remove the notify listener */
69 wl_list_remove(&sub_obj->destroy_listener.link);
70 sub_obj->destroy_listener.notify = NULL;
71
72 wl_list_remove(&sub_obj->subscription_link);
73 free(sub_obj);
74}
75
76/** Destroy the timeline subscription and all timeline subscription objects
77 * associated with it.
78 *
79 * Called when (before) the subscription is destroyed.
80 *
81 * @ingroup internal-log
82 */
83void
84weston_timeline_destroy_subscription(struct weston_log_subscription *sub,
85 void *user_data)
86{
87 struct weston_timeline_subscription *tl_sub =
88 weston_log_subscription_get_data(sub);
89 struct weston_timeline_subscription_object *sub_obj, *tmp_sub_obj;
90
91 if (!tl_sub)
92 return;
93
94 wl_list_for_each_safe(sub_obj, tmp_sub_obj,
95 &tl_sub->objects, subscription_link)
96 weston_timeline_destroy_subscription_object(sub_obj);
97
98 free(tl_sub);
99}
100
101static bool
102weston_timeline_check_object_refresh(struct weston_timeline_subscription_object *obj)
103{
104 if (obj->force_refresh == true) {
105 obj->force_refresh = false;
106 return true;
107 }
108 return false;
109}
110
111static struct weston_timeline_subscription_object *
112weston_timeline_subscription_search(struct weston_timeline_subscription *tl_sub,
113 void *object)
114{
115 struct weston_timeline_subscription_object *sub_obj;
116
117 wl_list_for_each(sub_obj, &tl_sub->objects, subscription_link)
118 if (sub_obj->object == object)
119 return sub_obj;
120
121 return NULL;
122}
123
124static struct weston_timeline_subscription_object *
125weston_timeline_subscription_object_create(void *object,
126 struct weston_timeline_subscription *tm_sub)
127{
128 struct weston_timeline_subscription_object *sub_obj;
129
130 sub_obj = zalloc(sizeof(*sub_obj));
131 sub_obj->id = ++tm_sub->next_id;
132 sub_obj->object = object;
133
134 /* when the object is created so that it has the chance to display the
135 * object ID, we set the refresh status; it will only be re-freshed by
136 * the backend (or part parts) when the underlying objects has suffered
137 * modifications */
138 sub_obj->force_refresh = true;
139
140 wl_list_insert(&tm_sub->objects, &sub_obj->subscription_link);
141
142 return sub_obj;
143}
144
145static void
146weston_timeline_destroy_subscription_object_notify(struct wl_listener *listener, void *data)
147{
148 struct weston_timeline_subscription_object *sub_obj;
149
150 sub_obj = wl_container_of(listener, sub_obj, destroy_listener);
151 weston_timeline_destroy_subscription_object(sub_obj);
152}
153
154static struct weston_timeline_subscription_object *
155weston_timeline_subscription_output_ensure(struct weston_timeline_subscription *tl_sub,
156 struct weston_output *output)
157{
158 struct weston_timeline_subscription_object *sub_obj;
159
160 sub_obj = weston_timeline_subscription_search(tl_sub, output);
161 if (!sub_obj) {
162 sub_obj = weston_timeline_subscription_object_create(output, tl_sub);
163
164 sub_obj->destroy_listener.notify =
165 weston_timeline_destroy_subscription_object_notify;
166 wl_signal_add(&output->destroy_signal,
167 &sub_obj->destroy_listener);
168 }
169 return sub_obj;
170}
171
172static struct weston_timeline_subscription_object *
173weston_timeline_subscription_surface_ensure(struct weston_timeline_subscription *tl_sub,
174 struct weston_surface *surface)
175{
176 struct weston_timeline_subscription_object *sub_obj;
177
178 sub_obj = weston_timeline_subscription_search(tl_sub, surface);
179 if (!sub_obj) {
180 sub_obj = weston_timeline_subscription_object_create(surface, tl_sub);
181
182 sub_obj->destroy_listener.notify =
183 weston_timeline_destroy_subscription_object_notify;
184 wl_signal_add(&surface->destroy_signal,
185 &sub_obj->destroy_listener);
186 }
187
188 return sub_obj;
189}
190
191static void
192fprint_quoted_string(struct weston_log_subscription *sub, const char *str)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200193{
194 if (!str) {
Marius Vladfb10ed72019-09-05 14:20:43 +0300195 weston_log_subscription_printf(sub, "null");
Pekka Paalanenb5026542014-11-12 15:09:24 +0200196 return;
197 }
198
Marius Vladfb10ed72019-09-05 14:20:43 +0300199 weston_log_subscription_printf(sub, "\"%s\"", str);
200}
201
202static void
203emit_weston_output_print_id(struct weston_log_subscription *sub,
204 struct weston_timeline_subscription_object *sub_obj,
205 const char *name)
206{
207 if (!weston_timeline_check_object_refresh(sub_obj))
208 return;
209
210 weston_log_subscription_printf(sub, "{ \"id\":%u, "
211 "\"type\":\"weston_output\", \"name\":", sub_obj->id);
212 fprint_quoted_string(sub, name);
213 weston_log_subscription_printf(sub, " }\n");
Pekka Paalanenb5026542014-11-12 15:09:24 +0200214}
215
216static int
217emit_weston_output(struct timeline_emit_context *ctx, void *obj)
218{
Marius Vladfb10ed72019-09-05 14:20:43 +0300219 struct weston_log_subscription *sub = ctx->subscription;
220 struct weston_output *output = obj;
221 struct weston_timeline_subscription_object *sub_obj;
222 struct weston_timeline_subscription *tl_sub;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200223
Marius Vladfb10ed72019-09-05 14:20:43 +0300224 tl_sub = weston_log_subscription_get_data(sub);
225 sub_obj = weston_timeline_subscription_output_ensure(tl_sub, output);
226 emit_weston_output_print_id(sub, sub_obj, output->name);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200227
Marius Vladfb10ed72019-09-05 14:20:43 +0300228 assert(sub_obj->id != 0);
229 fprintf(ctx->cur, "\"wo\":%u", sub_obj->id);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200230
231 return 1;
232}
233
Marius Vladfb10ed72019-09-05 14:20:43 +0300234
Pekka Paalanenb5026542014-11-12 15:09:24 +0200235static void
Marius Vladfb10ed72019-09-05 14:20:43 +0300236check_weston_surface_description(struct weston_log_subscription *sub,
237 struct weston_surface *s,
238 struct weston_timeline_subscription *tm_sub,
239 struct weston_timeline_subscription_object *sub_obj)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200240{
241 struct weston_surface *mains;
242 char d[512];
243 char mainstr[32];
244
Marius Vladfb10ed72019-09-05 14:20:43 +0300245 if (!weston_timeline_check_object_refresh(sub_obj))
Pekka Paalanenb5026542014-11-12 15:09:24 +0200246 return;
247
248 mains = weston_surface_get_main_surface(s);
249 if (mains != s) {
Marius Vladfb10ed72019-09-05 14:20:43 +0300250 struct weston_timeline_subscription_object *new_sub_obj;
251
252 new_sub_obj = weston_timeline_subscription_surface_ensure(tm_sub, mains);
253 check_weston_surface_description(sub, mains, tm_sub, new_sub_obj);
254 if (snprintf(mainstr, sizeof(mainstr), ", \"main_surface\":%u",
255 new_sub_obj->id) < 0)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200256 mainstr[0] = '\0';
257 } else {
258 mainstr[0] = '\0';
259 }
260
261 if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0)
262 d[0] = '\0';
263
Marius Vladfb10ed72019-09-05 14:20:43 +0300264 weston_log_subscription_printf(sub, "{ \"id\":%u, "
265 "\"type\":\"weston_surface\", \"desc\":",
266 sub_obj->id);
267 fprint_quoted_string(sub, d[0] ? d : NULL);
268 weston_log_subscription_printf(sub, "%s }\n", mainstr);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200269}
270
271static int
272emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
273{
Marius Vladfb10ed72019-09-05 14:20:43 +0300274 struct weston_log_subscription *sub = ctx->subscription;
275 struct weston_surface *surface = obj;
276 struct weston_timeline_subscription_object *sub_obj;
277 struct weston_timeline_subscription *tl_sub;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200278
Marius Vladfb10ed72019-09-05 14:20:43 +0300279 tl_sub = weston_log_subscription_get_data(sub);
280 sub_obj = weston_timeline_subscription_surface_ensure(tl_sub, surface);
281 check_weston_surface_description(sub, surface, tl_sub, sub_obj);
282
283 assert(sub_obj->id != 0);
284 fprintf(ctx->cur, "\"ws\":%u", sub_obj->id);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200285
286 return 1;
287}
288
289static int
290emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
291{
292 struct timespec *ts = obj;
293
294 fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]",
295 (int64_t)ts->tv_sec, ts->tv_nsec);
296
297 return 1;
298}
299
Alexandros Frantzis75d38ef2017-09-27 15:09:13 +0300300static int
301emit_gpu_timestamp(struct timeline_emit_context *ctx, void *obj)
302{
303 struct timespec *ts = obj;
304
305 fprintf(ctx->cur, "\"gpu\":[%" PRId64 ", %ld]",
306 (int64_t)ts->tv_sec, ts->tv_nsec);
307
308 return 1;
309}
310
Marius Vlad5de92972019-10-15 13:25:41 +0300311static struct weston_timeline_subscription_object *
312weston_timeline_get_subscription_object(struct weston_log_subscription *sub,
313 void *object)
314{
315 struct weston_timeline_subscription *tl_sub;
316
317 tl_sub = weston_log_subscription_get_data(sub);
318 if (!tl_sub)
319 return NULL;
320
321 return weston_timeline_subscription_search(tl_sub, object);
322}
323
324WL_EXPORT void
325weston_timeline_refresh_subscription_objects(struct weston_compositor *wc,
326 void *object)
327{
328 struct weston_log_subscription *sub = NULL;
329
330 while ((sub = weston_log_subscription_iterate(wc->timeline, sub))) {
331 struct weston_timeline_subscription_object *sub_obj;
332
333 sub_obj = weston_timeline_get_subscription_object(sub, object);
334 if (sub_obj)
335 sub_obj->force_refresh = true;
336 }
337}
338
Pekka Paalanenb5026542014-11-12 15:09:24 +0200339typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
340
341static const type_func type_dispatch[] = {
342 [TLT_OUTPUT] = emit_weston_output,
343 [TLT_SURFACE] = emit_weston_surface,
344 [TLT_VBLANK] = emit_vblank_timestamp,
Alexandros Frantzis75d38ef2017-09-27 15:09:13 +0300345 [TLT_GPU] = emit_gpu_timestamp,
Pekka Paalanenb5026542014-11-12 15:09:24 +0200346};
347
348WL_EXPORT void
Marius Vlad3203ff62019-09-05 14:56:12 +0300349weston_timeline_point(struct weston_log_scope *timeline_scope,
350 const char *name, ...)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200351{
Pekka Paalanenb5026542014-11-12 15:09:24 +0200352 struct timespec ts;
353 enum timeline_type otype;
354 void *obj;
355 char buf[512];
Marius Vlad3203ff62019-09-05 14:56:12 +0300356 struct weston_log_subscription *sub = NULL;
357
358 if (!weston_log_scope_is_enabled(timeline_scope))
359 return;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200360
Marius Vlad2a1b7862019-09-05 13:12:18 +0300361 clock_gettime(CLOCK_MONOTONIC, &ts);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200362
Marius Vlad3203ff62019-09-05 14:56:12 +0300363 while ((sub = weston_log_subscription_iterate(timeline_scope, sub))) {
364 va_list argp;
365 struct timeline_emit_context ctx = {};
Pekka Paalanenb5026542014-11-12 15:09:24 +0200366
Marius Vlad3203ff62019-09-05 14:56:12 +0300367 memset(buf, 0, sizeof(buf));
368 ctx.cur = fmemopen(buf, sizeof(buf), "w");
369 ctx.subscription = sub;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200370
Marius Vlad3203ff62019-09-05 14:56:12 +0300371 if (!ctx.cur) {
372 weston_log("Timeline error in fmemopen, closing.\n");
373 return;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200374 }
Pekka Paalanenb5026542014-11-12 15:09:24 +0200375
Marius Vlad3203ff62019-09-05 14:56:12 +0300376 fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
377 (int64_t)ts.tv_sec, ts.tv_nsec, name);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200378
Marius Vlad3203ff62019-09-05 14:56:12 +0300379 va_start(argp, name);
380 while (1) {
381 otype = va_arg(argp, enum timeline_type);
382 if (otype == TLT_END)
383 break;
384
385 obj = va_arg(argp, void *);
386 if (type_dispatch[otype]) {
387 fprintf(ctx.cur, ", ");
388 type_dispatch[otype](&ctx, obj);
389 }
390 }
391 va_end(argp);
392
393 fprintf(ctx.cur, " }\n");
394 fflush(ctx.cur);
395 if (ferror(ctx.cur)) {
396 weston_log("Timeline error in constructing entry, closing.\n");
397 } else {
398 weston_log_subscription_printf(ctx.subscription, "%s", buf);
399 }
400
401 fclose(ctx.cur);
402
403 }
Pekka Paalanenb5026542014-11-12 15:09:24 +0200404}