blob: 8234c27cdd35b641630c0578c0ce2568aa55b68a [file] [log] [blame]
Pekka Paalanenb5026542014-11-12 15:09:24 +02001/*
2 * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
3 * Copyright © 2014 Collabora, Ltd.
4 *
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
35#include "timeline.h"
36#include "compositor.h"
Pekka Paalanen23a07002015-02-12 13:11:25 +020037#include "file-util.h"
Pekka Paalanenb5026542014-11-12 15:09:24 +020038
39struct timeline_log {
40 clock_t clk_id;
41 FILE *file;
42 unsigned series;
43 struct wl_listener compositor_destroy_listener;
44};
45
46WL_EXPORT int weston_timeline_enabled_;
47static struct timeline_log timeline_ = { CLOCK_MONOTONIC, NULL, 0 };
48
49static int
50weston_timeline_do_open(void)
51{
Pekka Paalanen23a07002015-02-12 13:11:25 +020052 const char *prefix = "weston-timeline-";
53 const char *suffix = ".log";
Pekka Paalanenb5026542014-11-12 15:09:24 +020054 char fname[1000];
Pekka Paalanenb5026542014-11-12 15:09:24 +020055
Pekka Paalanen23a07002015-02-12 13:11:25 +020056 timeline_.file = file_create_dated(prefix, suffix,
57 fname, sizeof(fname));
Pekka Paalanenb5026542014-11-12 15:09:24 +020058 if (!timeline_.file) {
Pekka Paalanen23a07002015-02-12 13:11:25 +020059 const char *msg;
60
61 switch (errno) {
62 case ETIME:
63 msg = "failure in datetime formatting";
64 break;
65 default:
66 msg = strerror(errno);
67 }
68
69 weston_log("Cannot open '%s*%s' for writing: %s\n",
70 prefix, suffix, msg);
Pekka Paalanenb5026542014-11-12 15:09:24 +020071 return -1;
72 }
73
74 weston_log("Opened timeline file '%s'\n", fname);
75
76 return 0;
77}
78
79static void
80timeline_notify_destroy(struct wl_listener *listener, void *data)
81{
82 weston_timeline_close();
83}
84
85void
86weston_timeline_open(struct weston_compositor *compositor)
87{
88 if (weston_timeline_enabled_)
89 return;
90
91 if (weston_timeline_do_open() < 0)
92 return;
93
94 timeline_.compositor_destroy_listener.notify = timeline_notify_destroy;
95 wl_signal_add(&compositor->destroy_signal,
96 &timeline_.compositor_destroy_listener);
97
98 if (++timeline_.series == 0)
99 ++timeline_.series;
100
101 weston_timeline_enabled_ = 1;
102}
103
104void
105weston_timeline_close(void)
106{
107 if (!weston_timeline_enabled_)
108 return;
109
110 weston_timeline_enabled_ = 0;
111
112 wl_list_remove(&timeline_.compositor_destroy_listener.link);
113
114 fclose(timeline_.file);
115 timeline_.file = NULL;
116 weston_log("Timeline log file closed.\n");
117}
118
119struct timeline_emit_context {
120 FILE *cur;
121 FILE *out;
122 unsigned series;
123};
124
125static unsigned
126timeline_new_id(void)
127{
128 static unsigned idc;
129
130 if (++idc == 0)
131 ++idc;
132
133 return idc;
134}
135
136static int
137check_series(struct timeline_emit_context *ctx,
138 struct weston_timeline_object *to)
139{
140 if (to->series == 0 || to->series != ctx->series) {
141 to->series = ctx->series;
142 to->id = timeline_new_id();
143 return 1;
144 }
145
146 if (to->force_refresh) {
147 to->force_refresh = 0;
148 return 1;
149 }
150
151 return 0;
152}
153
154static void
155fprint_quoted_string(FILE *fp, const char *str)
156{
157 if (!str) {
158 fprintf(fp, "null");
159 return;
160 }
161
162 fprintf(fp, "\"%s\"", str);
163}
164
165static int
166emit_weston_output(struct timeline_emit_context *ctx, void *obj)
167{
168 struct weston_output *o = obj;
169
170 if (check_series(ctx, &o->timeline)) {
171 fprintf(ctx->out, "{ \"id\":%u, "
172 "\"type\":\"weston_output\", \"name\":",
173 o->timeline.id);
174 fprint_quoted_string(ctx->out, o->name);
175 fprintf(ctx->out, " }\n");
176 }
177
178 fprintf(ctx->cur, "\"wo\":%u", o->timeline.id);
179
180 return 1;
181}
182
183static void
184check_weston_surface_description(struct timeline_emit_context *ctx,
185 struct weston_surface *s)
186{
187 struct weston_surface *mains;
188 char d[512];
189 char mainstr[32];
190
191 if (!check_series(ctx, &s->timeline))
192 return;
193
194 mains = weston_surface_get_main_surface(s);
195 if (mains != s) {
196 check_weston_surface_description(ctx, mains);
197 if (snprintf(mainstr, sizeof(mainstr),
198 ", \"main_surface\":%u", mains->timeline.id) < 0)
199 mainstr[0] = '\0';
200 } else {
201 mainstr[0] = '\0';
202 }
203
204 if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0)
205 d[0] = '\0';
206
207 fprintf(ctx->out, "{ \"id\":%u, "
208 "\"type\":\"weston_surface\", \"desc\":", s->timeline.id);
209 fprint_quoted_string(ctx->out, d[0] ? d : NULL);
210 fprintf(ctx->out, "%s }\n", mainstr);
211}
212
213static int
214emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
215{
216 struct weston_surface *s = obj;
217
218 check_weston_surface_description(ctx, s);
219 fprintf(ctx->cur, "\"ws\":%u", s->timeline.id);
220
221 return 1;
222}
223
224static int
225emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
226{
227 struct timespec *ts = obj;
228
229 fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]",
230 (int64_t)ts->tv_sec, ts->tv_nsec);
231
232 return 1;
233}
234
Alexandros Frantzis75d38ef2017-09-27 15:09:13 +0300235static int
236emit_gpu_timestamp(struct timeline_emit_context *ctx, void *obj)
237{
238 struct timespec *ts = obj;
239
240 fprintf(ctx->cur, "\"gpu\":[%" PRId64 ", %ld]",
241 (int64_t)ts->tv_sec, ts->tv_nsec);
242
243 return 1;
244}
245
Pekka Paalanenb5026542014-11-12 15:09:24 +0200246typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
247
248static const type_func type_dispatch[] = {
249 [TLT_OUTPUT] = emit_weston_output,
250 [TLT_SURFACE] = emit_weston_surface,
251 [TLT_VBLANK] = emit_vblank_timestamp,
Alexandros Frantzis75d38ef2017-09-27 15:09:13 +0300252 [TLT_GPU] = emit_gpu_timestamp,
Pekka Paalanenb5026542014-11-12 15:09:24 +0200253};
254
255WL_EXPORT void
256weston_timeline_point(const char *name, ...)
257{
258 va_list argp;
259 struct timespec ts;
260 enum timeline_type otype;
261 void *obj;
262 char buf[512];
263 struct timeline_emit_context ctx;
264
265 clock_gettime(timeline_.clk_id, &ts);
266
267 ctx.out = timeline_.file;
268 ctx.cur = fmemopen(buf, sizeof(buf), "w");
269 ctx.series = timeline_.series;
270
271 if (!ctx.cur) {
272 weston_log("Timeline error in fmemopen, closing.\n");
273 weston_timeline_close();
274 return;
275 }
276
277 fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
278 (int64_t)ts.tv_sec, ts.tv_nsec, name);
279
280 va_start(argp, name);
281 while (1) {
282 otype = va_arg(argp, enum timeline_type);
283 if (otype == TLT_END)
284 break;
285
286 obj = va_arg(argp, void *);
287 if (type_dispatch[otype]) {
288 fprintf(ctx.cur, ", ");
289 type_dispatch[otype](&ctx, obj);
290 }
291 }
292 va_end(argp);
293
294 fprintf(ctx.cur, " }\n");
295 fflush(ctx.cur);
296 if (ferror(ctx.cur)) {
297 weston_log("Timeline error in constructing entry, closing.\n");
298 weston_timeline_close();
299 } else {
300 fprintf(ctx.out, "%s", buf);
301 }
302
303 fclose(ctx.cur);
304}