• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
3  * Copyright © 2014, 2019 Collabora, Ltd.
4  *
5  * 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:
12  *
13  * 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.
25  */
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 
35 #include <libweston/libweston.h>
36 #include <libweston/weston-log.h>
37 #include "timeline.h"
38 #include "weston-log-internal.h"
39 
40 /**
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  */
73 struct timeline_emit_context {
74 	FILE *cur;
75 	struct weston_log_subscription *subscription;
76 };
77 
78 /** Create a timeline subscription and hang it off the subscription
79  *
80  * Called when the subscription is created.
81  *
82  * @ingroup internal-log
83  */
84 void
weston_timeline_create_subscription(struct weston_log_subscription * sub,void * user_data)85 weston_timeline_create_subscription(struct weston_log_subscription *sub,
86 		void *user_data)
87 {
88 	struct weston_timeline_subscription *tl_sub = zalloc(sizeof(*tl_sub));
89 	if (!tl_sub)
90 		return;
91 
92 	wl_list_init(&tl_sub->objects);
93 
94 	/* attach this timeline_subscription to it */
95 	weston_log_subscription_set_data(sub, tl_sub);
96 }
97 
98 static void
weston_timeline_destroy_subscription_object(struct weston_timeline_subscription_object * sub_obj)99 weston_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  */
116 void
weston_timeline_destroy_subscription(struct weston_log_subscription * sub,void * user_data)117 weston_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 
134 static bool
weston_timeline_check_object_refresh(struct weston_timeline_subscription_object * obj)135 weston_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 
144 static struct weston_timeline_subscription_object *
weston_timeline_subscription_search(struct weston_timeline_subscription * tl_sub,void * object)145 weston_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 
157 static struct weston_timeline_subscription_object *
weston_timeline_subscription_object_create(void * object,struct weston_timeline_subscription * tm_sub)158 weston_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 
178 static void
weston_timeline_destroy_subscription_object_notify(struct wl_listener * listener,void * data)179 weston_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 
187 static struct weston_timeline_subscription_object *
weston_timeline_subscription_output_ensure(struct weston_timeline_subscription * tl_sub,struct weston_output * output)188 weston_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 
205 static struct weston_timeline_subscription_object *
weston_timeline_subscription_surface_ensure(struct weston_timeline_subscription * tl_sub,struct weston_surface * surface)206 weston_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 
224 static void
fprint_quoted_string(struct weston_log_subscription * sub,const char * str)225 fprint_quoted_string(struct weston_log_subscription *sub, const char *str)
226 {
227 	if (!str) {
228 		weston_log_subscription_printf(sub, "null");
229 		return;
230 	}
231 
232 	weston_log_subscription_printf(sub, "\"%s\"", str);
233 }
234 
235 static void
emit_weston_output_print_id(struct weston_log_subscription * sub,struct weston_timeline_subscription_object * sub_obj,const char * name)236 emit_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");
247 }
248 
249 static int
emit_weston_output(struct timeline_emit_context * ctx,void * obj)250 emit_weston_output(struct timeline_emit_context *ctx, void *obj)
251 {
252 	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;
256 
257 	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);
260 
261 	assert(sub_obj->id != 0);
262 	fprintf(ctx->cur, "\"wo\":%u", sub_obj->id);
263 
264 	return 1;
265 }
266 
267 
268 static void
check_weston_surface_description(struct weston_log_subscription * sub,struct weston_surface * s,struct weston_timeline_subscription * tm_sub,struct weston_timeline_subscription_object * sub_obj)269 check_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)
273 {
274 	struct weston_surface *mains;
275 	char d[512];
276 	char mainstr[32];
277 
278 	if (!weston_timeline_check_object_refresh(sub_obj))
279 		return;
280 
281 	mains = weston_surface_get_main_surface(s);
282 	if (mains != s) {
283 		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)
289 			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 
297 	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);
302 }
303 
304 static int
emit_weston_surface(struct timeline_emit_context * ctx,void * obj)305 emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
306 {
307 	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;
311 
312 	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);
318 
319 	return 1;
320 }
321 
322 static int
emit_vblank_timestamp(struct timeline_emit_context * ctx,void * obj)323 emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
324 {
325 	struct timespec *ts = obj;
326 
327 	fprintf(ctx->cur, "\"vblank_monotonic\":[%" PRId64 ", %ld]",
328 		(int64_t)ts->tv_sec, ts->tv_nsec);
329 
330 	return 1;
331 }
332 
333 static int
emit_gpu_timestamp(struct timeline_emit_context * ctx,void * obj)334 emit_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 
344 static struct weston_timeline_subscription_object *
weston_timeline_get_subscription_object(struct weston_log_subscription * sub,void * object)345 weston_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 
357 /** 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  */
369 WL_EXPORT void
weston_timeline_refresh_subscription_objects(struct weston_compositor * wc,void * object)370 weston_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 
384 typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
385 
386 static const type_func type_dispatch[] = {
387 	[TLT_OUTPUT] = emit_weston_output,
388 	[TLT_SURFACE] = emit_weston_surface,
389 	[TLT_VBLANK] = emit_vblank_timestamp,
390 	[TLT_GPU] = emit_gpu_timestamp,
391 };
392 
393 /** 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  */
405 WL_EXPORT void
weston_timeline_point(struct weston_log_scope * timeline_scope,const char * name,...)406 weston_timeline_point(struct weston_log_scope *timeline_scope,
407 		      const char *name, ...)
408 {
409 	struct timespec ts;
410 	enum timeline_type otype;
411 	void *obj;
412 	char buf[512];
413 	struct weston_log_subscription *sub = NULL;
414 
415 	if (!weston_log_scope_is_enabled(timeline_scope))
416 		return;
417 
418 	clock_gettime(CLOCK_MONOTONIC, &ts);
419 
420 	while ((sub = weston_log_subscription_iterate(timeline_scope, sub))) {
421 		va_list argp;
422 		struct timeline_emit_context ctx = {};
423 
424 		memset(buf, 0, sizeof(buf));
425 		ctx.cur = fmemopen(buf, sizeof(buf), "w");
426 		ctx.subscription = sub;
427 
428 		if (!ctx.cur) {
429 			weston_log("Timeline error in fmemopen, closing.\n");
430 			return;
431 		}
432 
433 		fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
434 				(int64_t)ts.tv_sec, ts.tv_nsec, name);
435 
436 		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 	}
461 }
462