blob: 35ccc523a982aa249f9fa3393ba2d3c2e3b8d7f7 [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
40WL_EXPORT int weston_timeline_enabled_;
Pekka Paalanenb5026542014-11-12 15:09:24 +020041
42struct timeline_emit_context {
43 FILE *cur;
Marius Vladfb10ed72019-09-05 14:20:43 +030044 struct weston_log_subscription *subscription;
Pekka Paalanenb5026542014-11-12 15:09:24 +020045};
46
Marius Vladfb10ed72019-09-05 14:20:43 +030047/** Create a timeline subscription and hang it off the subscription
48 *
49 * Called when the subscription is created.
50 *
51 * @ingroup internal-log
52 */
53void
54weston_timeline_create_subscription(struct weston_log_subscription *sub,
55 void *user_data)
Pekka Paalanenb5026542014-11-12 15:09:24 +020056{
Marius Vladfb10ed72019-09-05 14:20:43 +030057 struct weston_timeline_subscription *tl_sub = zalloc(sizeof(*tl_sub));
58 if (!tl_sub)
59 return;
Pekka Paalanenb5026542014-11-12 15:09:24 +020060
Marius Vladfb10ed72019-09-05 14:20:43 +030061 wl_list_init(&tl_sub->objects);
Pekka Paalanenb5026542014-11-12 15:09:24 +020062
Marius Vladfb10ed72019-09-05 14:20:43 +030063 /* attach this timeline_subscription to it */
64 weston_log_subscription_set_data(sub, tl_sub);
Pekka Paalanenb5026542014-11-12 15:09:24 +020065}
66
67static void
Marius Vladfb10ed72019-09-05 14:20:43 +030068weston_timeline_destroy_subscription_object(struct weston_timeline_subscription_object *sub_obj)
69{
70 /* remove the notify listener */
71 wl_list_remove(&sub_obj->destroy_listener.link);
72 sub_obj->destroy_listener.notify = NULL;
73
74 wl_list_remove(&sub_obj->subscription_link);
75 free(sub_obj);
76}
77
78/** Destroy the timeline subscription and all timeline subscription objects
79 * associated with it.
80 *
81 * Called when (before) the subscription is destroyed.
82 *
83 * @ingroup internal-log
84 */
85void
86weston_timeline_destroy_subscription(struct weston_log_subscription *sub,
87 void *user_data)
88{
89 struct weston_timeline_subscription *tl_sub =
90 weston_log_subscription_get_data(sub);
91 struct weston_timeline_subscription_object *sub_obj, *tmp_sub_obj;
92
93 if (!tl_sub)
94 return;
95
96 wl_list_for_each_safe(sub_obj, tmp_sub_obj,
97 &tl_sub->objects, subscription_link)
98 weston_timeline_destroy_subscription_object(sub_obj);
99
100 free(tl_sub);
101}
102
103static bool
104weston_timeline_check_object_refresh(struct weston_timeline_subscription_object *obj)
105{
106 if (obj->force_refresh == true) {
107 obj->force_refresh = false;
108 return true;
109 }
110 return false;
111}
112
113static struct weston_timeline_subscription_object *
114weston_timeline_subscription_search(struct weston_timeline_subscription *tl_sub,
115 void *object)
116{
117 struct weston_timeline_subscription_object *sub_obj;
118
119 wl_list_for_each(sub_obj, &tl_sub->objects, subscription_link)
120 if (sub_obj->object == object)
121 return sub_obj;
122
123 return NULL;
124}
125
126static struct weston_timeline_subscription_object *
127weston_timeline_subscription_object_create(void *object,
128 struct weston_timeline_subscription *tm_sub)
129{
130 struct weston_timeline_subscription_object *sub_obj;
131
132 sub_obj = zalloc(sizeof(*sub_obj));
133 sub_obj->id = ++tm_sub->next_id;
134 sub_obj->object = object;
135
136 /* when the object is created so that it has the chance to display the
137 * object ID, we set the refresh status; it will only be re-freshed by
138 * the backend (or part parts) when the underlying objects has suffered
139 * modifications */
140 sub_obj->force_refresh = true;
141
142 wl_list_insert(&tm_sub->objects, &sub_obj->subscription_link);
143
144 return sub_obj;
145}
146
147static void
148weston_timeline_destroy_subscription_object_notify(struct wl_listener *listener, void *data)
149{
150 struct weston_timeline_subscription_object *sub_obj;
151
152 sub_obj = wl_container_of(listener, sub_obj, destroy_listener);
153 weston_timeline_destroy_subscription_object(sub_obj);
154}
155
156static struct weston_timeline_subscription_object *
157weston_timeline_subscription_output_ensure(struct weston_timeline_subscription *tl_sub,
158 struct weston_output *output)
159{
160 struct weston_timeline_subscription_object *sub_obj;
161
162 sub_obj = weston_timeline_subscription_search(tl_sub, output);
163 if (!sub_obj) {
164 sub_obj = weston_timeline_subscription_object_create(output, tl_sub);
165
166 sub_obj->destroy_listener.notify =
167 weston_timeline_destroy_subscription_object_notify;
168 wl_signal_add(&output->destroy_signal,
169 &sub_obj->destroy_listener);
170 }
171 return sub_obj;
172}
173
174static struct weston_timeline_subscription_object *
175weston_timeline_subscription_surface_ensure(struct weston_timeline_subscription *tl_sub,
176 struct weston_surface *surface)
177{
178 struct weston_timeline_subscription_object *sub_obj;
179
180 sub_obj = weston_timeline_subscription_search(tl_sub, surface);
181 if (!sub_obj) {
182 sub_obj = weston_timeline_subscription_object_create(surface, tl_sub);
183
184 sub_obj->destroy_listener.notify =
185 weston_timeline_destroy_subscription_object_notify;
186 wl_signal_add(&surface->destroy_signal,
187 &sub_obj->destroy_listener);
188 }
189
190 return sub_obj;
191}
192
193static void
194fprint_quoted_string(struct weston_log_subscription *sub, const char *str)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200195{
196 if (!str) {
Marius Vladfb10ed72019-09-05 14:20:43 +0300197 weston_log_subscription_printf(sub, "null");
Pekka Paalanenb5026542014-11-12 15:09:24 +0200198 return;
199 }
200
Marius Vladfb10ed72019-09-05 14:20:43 +0300201 weston_log_subscription_printf(sub, "\"%s\"", str);
202}
203
204static void
205emit_weston_output_print_id(struct weston_log_subscription *sub,
206 struct weston_timeline_subscription_object *sub_obj,
207 const char *name)
208{
209 if (!weston_timeline_check_object_refresh(sub_obj))
210 return;
211
212 weston_log_subscription_printf(sub, "{ \"id\":%u, "
213 "\"type\":\"weston_output\", \"name\":", sub_obj->id);
214 fprint_quoted_string(sub, name);
215 weston_log_subscription_printf(sub, " }\n");
Pekka Paalanenb5026542014-11-12 15:09:24 +0200216}
217
218static int
219emit_weston_output(struct timeline_emit_context *ctx, void *obj)
220{
Marius Vladfb10ed72019-09-05 14:20:43 +0300221 struct weston_log_subscription *sub = ctx->subscription;
222 struct weston_output *output = obj;
223 struct weston_timeline_subscription_object *sub_obj;
224 struct weston_timeline_subscription *tl_sub;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200225
Marius Vladfb10ed72019-09-05 14:20:43 +0300226 tl_sub = weston_log_subscription_get_data(sub);
227 sub_obj = weston_timeline_subscription_output_ensure(tl_sub, output);
228 emit_weston_output_print_id(sub, sub_obj, output->name);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200229
Marius Vladfb10ed72019-09-05 14:20:43 +0300230 assert(sub_obj->id != 0);
231 fprintf(ctx->cur, "\"wo\":%u", sub_obj->id);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200232
233 return 1;
234}
235
Marius Vladfb10ed72019-09-05 14:20:43 +0300236
Pekka Paalanenb5026542014-11-12 15:09:24 +0200237static void
Marius Vladfb10ed72019-09-05 14:20:43 +0300238check_weston_surface_description(struct weston_log_subscription *sub,
239 struct weston_surface *s,
240 struct weston_timeline_subscription *tm_sub,
241 struct weston_timeline_subscription_object *sub_obj)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200242{
243 struct weston_surface *mains;
244 char d[512];
245 char mainstr[32];
246
Marius Vladfb10ed72019-09-05 14:20:43 +0300247 if (!weston_timeline_check_object_refresh(sub_obj))
Pekka Paalanenb5026542014-11-12 15:09:24 +0200248 return;
249
250 mains = weston_surface_get_main_surface(s);
251 if (mains != s) {
Marius Vladfb10ed72019-09-05 14:20:43 +0300252 struct weston_timeline_subscription_object *new_sub_obj;
253
254 new_sub_obj = weston_timeline_subscription_surface_ensure(tm_sub, mains);
255 check_weston_surface_description(sub, mains, tm_sub, new_sub_obj);
256 if (snprintf(mainstr, sizeof(mainstr), ", \"main_surface\":%u",
257 new_sub_obj->id) < 0)
Pekka Paalanenb5026542014-11-12 15:09:24 +0200258 mainstr[0] = '\0';
259 } else {
260 mainstr[0] = '\0';
261 }
262
263 if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0)
264 d[0] = '\0';
265
Marius Vladfb10ed72019-09-05 14:20:43 +0300266 weston_log_subscription_printf(sub, "{ \"id\":%u, "
267 "\"type\":\"weston_surface\", \"desc\":",
268 sub_obj->id);
269 fprint_quoted_string(sub, d[0] ? d : NULL);
270 weston_log_subscription_printf(sub, "%s }\n", mainstr);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200271}
272
273static int
274emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
275{
Marius Vladfb10ed72019-09-05 14:20:43 +0300276 struct weston_log_subscription *sub = ctx->subscription;
277 struct weston_surface *surface = obj;
278 struct weston_timeline_subscription_object *sub_obj;
279 struct weston_timeline_subscription *tl_sub;
Pekka Paalanenb5026542014-11-12 15:09:24 +0200280
Marius Vladfb10ed72019-09-05 14:20:43 +0300281 tl_sub = weston_log_subscription_get_data(sub);
282 sub_obj = weston_timeline_subscription_surface_ensure(tl_sub, surface);
283 check_weston_surface_description(sub, surface, tl_sub, sub_obj);
284
285 assert(sub_obj->id != 0);
286 fprintf(ctx->cur, "\"ws\":%u", sub_obj->id);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200287
288 return 1;
289}
290
291static int
292emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
293{
294 struct timespec *ts = obj;
295
296 fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]",
297 (int64_t)ts->tv_sec, ts->tv_nsec);
298
299 return 1;
300}
301
Alexandros Frantzis75d38ef2017-09-27 15:09:13 +0300302static int
303emit_gpu_timestamp(struct timeline_emit_context *ctx, void *obj)
304{
305 struct timespec *ts = obj;
306
307 fprintf(ctx->cur, "\"gpu\":[%" PRId64 ", %ld]",
308 (int64_t)ts->tv_sec, ts->tv_nsec);
309
310 return 1;
311}
312
Pekka Paalanenb5026542014-11-12 15:09:24 +0200313typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
314
315static const type_func type_dispatch[] = {
316 [TLT_OUTPUT] = emit_weston_output,
317 [TLT_SURFACE] = emit_weston_surface,
318 [TLT_VBLANK] = emit_vblank_timestamp,
Alexandros Frantzis75d38ef2017-09-27 15:09:13 +0300319 [TLT_GPU] = emit_gpu_timestamp,
Pekka Paalanenb5026542014-11-12 15:09:24 +0200320};
321
322WL_EXPORT void
323weston_timeline_point(const char *name, ...)
324{
325 va_list argp;
326 struct timespec ts;
327 enum timeline_type otype;
328 void *obj;
329 char buf[512];
330 struct timeline_emit_context ctx;
331
Marius Vlad2a1b7862019-09-05 13:12:18 +0300332 clock_gettime(CLOCK_MONOTONIC, &ts);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200333
Pekka Paalanenb5026542014-11-12 15:09:24 +0200334 ctx.cur = fmemopen(buf, sizeof(buf), "w");
Pekka Paalanenb5026542014-11-12 15:09:24 +0200335
336 if (!ctx.cur) {
337 weston_log("Timeline error in fmemopen, closing.\n");
Pekka Paalanenb5026542014-11-12 15:09:24 +0200338 return;
339 }
340
341 fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
342 (int64_t)ts.tv_sec, ts.tv_nsec, name);
343
344 va_start(argp, name);
345 while (1) {
346 otype = va_arg(argp, enum timeline_type);
347 if (otype == TLT_END)
348 break;
349
350 obj = va_arg(argp, void *);
351 if (type_dispatch[otype]) {
352 fprintf(ctx.cur, ", ");
353 type_dispatch[otype](&ctx, obj);
354 }
355 }
356 va_end(argp);
357
358 fprintf(ctx.cur, " }\n");
359 fflush(ctx.cur);
360 if (ferror(ctx.cur)) {
361 weston_log("Timeline error in constructing entry, closing.\n");
Pekka Paalanenb5026542014-11-12 15:09:24 +0200362 } else {
Marius Vladfb10ed72019-09-05 14:20:43 +0300363 weston_log_subscription_printf(ctx.subscription, "%s", buf);
Pekka Paalanenb5026542014-11-12 15:09:24 +0200364 }
365
366 fclose(ctx.cur);
367}