blob: c60213ea732320e1476c62227a829fb0873a0d7a [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 *
5 * Permission to use, copy, modify, distribute, and sell this software and
6 * its documentation for any purpose is hereby granted without fee, provided
7 * that the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of the copyright holders not be used in
10 * advertising or publicity pertaining to distribution of the software
11 * without specific, written prior permission. The copyright holders make
12 * no representations about the suitability of this software for any
13 * purpose. It is provided "as is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
16 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
20 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */
23
24#include "config.h"
25
26#include <stdio.h>
27#include <errno.h>
28#include <string.h>
29#include <time.h>
30#include <assert.h>
31
32#include "timeline.h"
33#include "compositor.h"
Pekka Paalanen23a07002015-02-12 13:11:25 +020034#include "file-util.h"
Pekka Paalanenb5026542014-11-12 15:09:24 +020035
36struct timeline_log {
37 clock_t clk_id;
38 FILE *file;
39 unsigned series;
40 struct wl_listener compositor_destroy_listener;
41};
42
43WL_EXPORT int weston_timeline_enabled_;
44static struct timeline_log timeline_ = { CLOCK_MONOTONIC, NULL, 0 };
45
46static int
47weston_timeline_do_open(void)
48{
Pekka Paalanen23a07002015-02-12 13:11:25 +020049 const char *prefix = "weston-timeline-";
50 const char *suffix = ".log";
Pekka Paalanenb5026542014-11-12 15:09:24 +020051 char fname[1000];
Pekka Paalanenb5026542014-11-12 15:09:24 +020052
Pekka Paalanen23a07002015-02-12 13:11:25 +020053 timeline_.file = file_create_dated(prefix, suffix,
54 fname, sizeof(fname));
Pekka Paalanenb5026542014-11-12 15:09:24 +020055 if (!timeline_.file) {
Pekka Paalanen23a07002015-02-12 13:11:25 +020056 const char *msg;
57
58 switch (errno) {
59 case ETIME:
60 msg = "failure in datetime formatting";
61 break;
62 default:
63 msg = strerror(errno);
64 }
65
66 weston_log("Cannot open '%s*%s' for writing: %s\n",
67 prefix, suffix, msg);
Pekka Paalanenb5026542014-11-12 15:09:24 +020068 return -1;
69 }
70
71 weston_log("Opened timeline file '%s'\n", fname);
72
73 return 0;
74}
75
76static void
77timeline_notify_destroy(struct wl_listener *listener, void *data)
78{
79 weston_timeline_close();
80}
81
82void
83weston_timeline_open(struct weston_compositor *compositor)
84{
85 if (weston_timeline_enabled_)
86 return;
87
88 if (weston_timeline_do_open() < 0)
89 return;
90
91 timeline_.compositor_destroy_listener.notify = timeline_notify_destroy;
92 wl_signal_add(&compositor->destroy_signal,
93 &timeline_.compositor_destroy_listener);
94
95 if (++timeline_.series == 0)
96 ++timeline_.series;
97
98 weston_timeline_enabled_ = 1;
99}
100
101void
102weston_timeline_close(void)
103{
104 if (!weston_timeline_enabled_)
105 return;
106
107 weston_timeline_enabled_ = 0;
108
109 wl_list_remove(&timeline_.compositor_destroy_listener.link);
110
111 fclose(timeline_.file);
112 timeline_.file = NULL;
113 weston_log("Timeline log file closed.\n");
114}
115
116struct timeline_emit_context {
117 FILE *cur;
118 FILE *out;
119 unsigned series;
120};
121
122static unsigned
123timeline_new_id(void)
124{
125 static unsigned idc;
126
127 if (++idc == 0)
128 ++idc;
129
130 return idc;
131}
132
133static int
134check_series(struct timeline_emit_context *ctx,
135 struct weston_timeline_object *to)
136{
137 if (to->series == 0 || to->series != ctx->series) {
138 to->series = ctx->series;
139 to->id = timeline_new_id();
140 return 1;
141 }
142
143 if (to->force_refresh) {
144 to->force_refresh = 0;
145 return 1;
146 }
147
148 return 0;
149}
150
151static void
152fprint_quoted_string(FILE *fp, const char *str)
153{
154 if (!str) {
155 fprintf(fp, "null");
156 return;
157 }
158
159 fprintf(fp, "\"%s\"", str);
160}
161
162static int
163emit_weston_output(struct timeline_emit_context *ctx, void *obj)
164{
165 struct weston_output *o = obj;
166
167 if (check_series(ctx, &o->timeline)) {
168 fprintf(ctx->out, "{ \"id\":%u, "
169 "\"type\":\"weston_output\", \"name\":",
170 o->timeline.id);
171 fprint_quoted_string(ctx->out, o->name);
172 fprintf(ctx->out, " }\n");
173 }
174
175 fprintf(ctx->cur, "\"wo\":%u", o->timeline.id);
176
177 return 1;
178}
179
180static void
181check_weston_surface_description(struct timeline_emit_context *ctx,
182 struct weston_surface *s)
183{
184 struct weston_surface *mains;
185 char d[512];
186 char mainstr[32];
187
188 if (!check_series(ctx, &s->timeline))
189 return;
190
191 mains = weston_surface_get_main_surface(s);
192 if (mains != s) {
193 check_weston_surface_description(ctx, mains);
194 if (snprintf(mainstr, sizeof(mainstr),
195 ", \"main_surface\":%u", mains->timeline.id) < 0)
196 mainstr[0] = '\0';
197 } else {
198 mainstr[0] = '\0';
199 }
200
201 if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0)
202 d[0] = '\0';
203
204 fprintf(ctx->out, "{ \"id\":%u, "
205 "\"type\":\"weston_surface\", \"desc\":", s->timeline.id);
206 fprint_quoted_string(ctx->out, d[0] ? d : NULL);
207 fprintf(ctx->out, "%s }\n", mainstr);
208}
209
210static int
211emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
212{
213 struct weston_surface *s = obj;
214
215 check_weston_surface_description(ctx, s);
216 fprintf(ctx->cur, "\"ws\":%u", s->timeline.id);
217
218 return 1;
219}
220
221static int
222emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
223{
224 struct timespec *ts = obj;
225
226 fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]",
227 (int64_t)ts->tv_sec, ts->tv_nsec);
228
229 return 1;
230}
231
232typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
233
234static const type_func type_dispatch[] = {
235 [TLT_OUTPUT] = emit_weston_output,
236 [TLT_SURFACE] = emit_weston_surface,
237 [TLT_VBLANK] = emit_vblank_timestamp,
238};
239
240WL_EXPORT void
241weston_timeline_point(const char *name, ...)
242{
243 va_list argp;
244 struct timespec ts;
245 enum timeline_type otype;
246 void *obj;
247 char buf[512];
248 struct timeline_emit_context ctx;
249
250 clock_gettime(timeline_.clk_id, &ts);
251
252 ctx.out = timeline_.file;
253 ctx.cur = fmemopen(buf, sizeof(buf), "w");
254 ctx.series = timeline_.series;
255
256 if (!ctx.cur) {
257 weston_log("Timeline error in fmemopen, closing.\n");
258 weston_timeline_close();
259 return;
260 }
261
262 fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
263 (int64_t)ts.tv_sec, ts.tv_nsec, name);
264
265 va_start(argp, name);
266 while (1) {
267 otype = va_arg(argp, enum timeline_type);
268 if (otype == TLT_END)
269 break;
270
271 obj = va_arg(argp, void *);
272 if (type_dispatch[otype]) {
273 fprintf(ctx.cur, ", ");
274 type_dispatch[otype](&ctx, obj);
275 }
276 }
277 va_end(argp);
278
279 fprintf(ctx.cur, " }\n");
280 fflush(ctx.cur);
281 if (ferror(ctx.cur)) {
282 weston_log("Timeline error in constructing entry, closing.\n");
283 weston_timeline_close();
284 } else {
285 fprintf(ctx.out, "%s", buf);
286 }
287
288 fclose(ctx.cur);
289}