blob: bf69ba6cc49314550fda89920f97ab8b2d93d8a8 [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"
34
35struct timeline_log {
36 clock_t clk_id;
37 FILE *file;
38 unsigned series;
39 struct wl_listener compositor_destroy_listener;
40};
41
42WL_EXPORT int weston_timeline_enabled_;
43static struct timeline_log timeline_ = { CLOCK_MONOTONIC, NULL, 0 };
44
45static int
46weston_timeline_do_open(void)
47{
48 time_t t;
49 struct tm *tmp;
50 char fname[1000];
51 int ret;
52
53 t = time(NULL);
54 tmp = localtime(&t);
55 if (!tmp) {
56 weston_log("Conversion to local time failed, "
57 "cannot open timeline log file.\n");
58 return -1;
59 }
60
61 ret = strftime(fname, sizeof(fname),
62 "weston-timeline-%F_%H-%M-%S.log", tmp);
63 if (ret == 0) {
64 weston_log("Time formatting failed, "
65 "cannot open timeline log file.\n");
66 return -1;
67 }
68
69 timeline_.file = fopen(fname, "w");
70 if (!timeline_.file) {
71 weston_log("Cannot open '%s' for writing: %s\n",
72 fname, strerror(errno));
73 return -1;
74 }
75
76 weston_log("Opened timeline file '%s'\n", fname);
77
78 return 0;
79}
80
81static void
82timeline_notify_destroy(struct wl_listener *listener, void *data)
83{
84 weston_timeline_close();
85}
86
87void
88weston_timeline_open(struct weston_compositor *compositor)
89{
90 if (weston_timeline_enabled_)
91 return;
92
93 if (weston_timeline_do_open() < 0)
94 return;
95
96 timeline_.compositor_destroy_listener.notify = timeline_notify_destroy;
97 wl_signal_add(&compositor->destroy_signal,
98 &timeline_.compositor_destroy_listener);
99
100 if (++timeline_.series == 0)
101 ++timeline_.series;
102
103 weston_timeline_enabled_ = 1;
104}
105
106void
107weston_timeline_close(void)
108{
109 if (!weston_timeline_enabled_)
110 return;
111
112 weston_timeline_enabled_ = 0;
113
114 wl_list_remove(&timeline_.compositor_destroy_listener.link);
115
116 fclose(timeline_.file);
117 timeline_.file = NULL;
118 weston_log("Timeline log file closed.\n");
119}
120
121struct timeline_emit_context {
122 FILE *cur;
123 FILE *out;
124 unsigned series;
125};
126
127static unsigned
128timeline_new_id(void)
129{
130 static unsigned idc;
131
132 if (++idc == 0)
133 ++idc;
134
135 return idc;
136}
137
138static int
139check_series(struct timeline_emit_context *ctx,
140 struct weston_timeline_object *to)
141{
142 if (to->series == 0 || to->series != ctx->series) {
143 to->series = ctx->series;
144 to->id = timeline_new_id();
145 return 1;
146 }
147
148 if (to->force_refresh) {
149 to->force_refresh = 0;
150 return 1;
151 }
152
153 return 0;
154}
155
156static void
157fprint_quoted_string(FILE *fp, const char *str)
158{
159 if (!str) {
160 fprintf(fp, "null");
161 return;
162 }
163
164 fprintf(fp, "\"%s\"", str);
165}
166
167static int
168emit_weston_output(struct timeline_emit_context *ctx, void *obj)
169{
170 struct weston_output *o = obj;
171
172 if (check_series(ctx, &o->timeline)) {
173 fprintf(ctx->out, "{ \"id\":%u, "
174 "\"type\":\"weston_output\", \"name\":",
175 o->timeline.id);
176 fprint_quoted_string(ctx->out, o->name);
177 fprintf(ctx->out, " }\n");
178 }
179
180 fprintf(ctx->cur, "\"wo\":%u", o->timeline.id);
181
182 return 1;
183}
184
185static void
186check_weston_surface_description(struct timeline_emit_context *ctx,
187 struct weston_surface *s)
188{
189 struct weston_surface *mains;
190 char d[512];
191 char mainstr[32];
192
193 if (!check_series(ctx, &s->timeline))
194 return;
195
196 mains = weston_surface_get_main_surface(s);
197 if (mains != s) {
198 check_weston_surface_description(ctx, mains);
199 if (snprintf(mainstr, sizeof(mainstr),
200 ", \"main_surface\":%u", mains->timeline.id) < 0)
201 mainstr[0] = '\0';
202 } else {
203 mainstr[0] = '\0';
204 }
205
206 if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0)
207 d[0] = '\0';
208
209 fprintf(ctx->out, "{ \"id\":%u, "
210 "\"type\":\"weston_surface\", \"desc\":", s->timeline.id);
211 fprint_quoted_string(ctx->out, d[0] ? d : NULL);
212 fprintf(ctx->out, "%s }\n", mainstr);
213}
214
215static int
216emit_weston_surface(struct timeline_emit_context *ctx, void *obj)
217{
218 struct weston_surface *s = obj;
219
220 check_weston_surface_description(ctx, s);
221 fprintf(ctx->cur, "\"ws\":%u", s->timeline.id);
222
223 return 1;
224}
225
226static int
227emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj)
228{
229 struct timespec *ts = obj;
230
231 fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]",
232 (int64_t)ts->tv_sec, ts->tv_nsec);
233
234 return 1;
235}
236
237typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj);
238
239static const type_func type_dispatch[] = {
240 [TLT_OUTPUT] = emit_weston_output,
241 [TLT_SURFACE] = emit_weston_surface,
242 [TLT_VBLANK] = emit_vblank_timestamp,
243};
244
245WL_EXPORT void
246weston_timeline_point(const char *name, ...)
247{
248 va_list argp;
249 struct timespec ts;
250 enum timeline_type otype;
251 void *obj;
252 char buf[512];
253 struct timeline_emit_context ctx;
254
255 clock_gettime(timeline_.clk_id, &ts);
256
257 ctx.out = timeline_.file;
258 ctx.cur = fmemopen(buf, sizeof(buf), "w");
259 ctx.series = timeline_.series;
260
261 if (!ctx.cur) {
262 weston_log("Timeline error in fmemopen, closing.\n");
263 weston_timeline_close();
264 return;
265 }
266
267 fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"",
268 (int64_t)ts.tv_sec, ts.tv_nsec, name);
269
270 va_start(argp, name);
271 while (1) {
272 otype = va_arg(argp, enum timeline_type);
273 if (otype == TLT_END)
274 break;
275
276 obj = va_arg(argp, void *);
277 if (type_dispatch[otype]) {
278 fprintf(ctx.cur, ", ");
279 type_dispatch[otype](&ctx, obj);
280 }
281 }
282 va_end(argp);
283
284 fprintf(ctx.cur, " }\n");
285 fflush(ctx.cur);
286 if (ferror(ctx.cur)) {
287 weston_log("Timeline error in constructing entry, closing.\n");
288 weston_timeline_close();
289 } else {
290 fprintf(ctx.out, "%s", buf);
291 }
292
293 fclose(ctx.cur);
294}