/* * Copyright © 2014 Pekka Paalanen * Copyright © 2014 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "config.h" #include #include #include #include #include #include "timeline.h" #include "compositor.h" #include "file-util.h" struct timeline_log { clock_t clk_id; FILE *file; unsigned series; struct wl_listener compositor_destroy_listener; }; WL_EXPORT int weston_timeline_enabled_; static struct timeline_log timeline_ = { CLOCK_MONOTONIC, NULL, 0 }; static int weston_timeline_do_open(void) { const char *prefix = "weston-timeline-"; const char *suffix = ".log"; char fname[1000]; timeline_.file = file_create_dated(prefix, suffix, fname, sizeof(fname)); if (!timeline_.file) { const char *msg; switch (errno) { case ETIME: msg = "failure in datetime formatting"; break; default: msg = strerror(errno); } weston_log("Cannot open '%s*%s' for writing: %s\n", prefix, suffix, msg); return -1; } weston_log("Opened timeline file '%s'\n", fname); return 0; } static void timeline_notify_destroy(struct wl_listener *listener, void *data) { weston_timeline_close(); } void weston_timeline_open(struct weston_compositor *compositor) { if (weston_timeline_enabled_) return; if (weston_timeline_do_open() < 0) return; timeline_.compositor_destroy_listener.notify = timeline_notify_destroy; wl_signal_add(&compositor->destroy_signal, &timeline_.compositor_destroy_listener); if (++timeline_.series == 0) ++timeline_.series; weston_timeline_enabled_ = 1; } void weston_timeline_close(void) { if (!weston_timeline_enabled_) return; weston_timeline_enabled_ = 0; wl_list_remove(&timeline_.compositor_destroy_listener.link); fclose(timeline_.file); timeline_.file = NULL; weston_log("Timeline log file closed.\n"); } struct timeline_emit_context { FILE *cur; FILE *out; unsigned series; }; static unsigned timeline_new_id(void) { static unsigned idc; if (++idc == 0) ++idc; return idc; } static int check_series(struct timeline_emit_context *ctx, struct weston_timeline_object *to) { if (to->series == 0 || to->series != ctx->series) { to->series = ctx->series; to->id = timeline_new_id(); return 1; } if (to->force_refresh) { to->force_refresh = 0; return 1; } return 0; } static void fprint_quoted_string(FILE *fp, const char *str) { if (!str) { fprintf(fp, "null"); return; } fprintf(fp, "\"%s\"", str); } static int emit_weston_output(struct timeline_emit_context *ctx, void *obj) { struct weston_output *o = obj; if (check_series(ctx, &o->timeline)) { fprintf(ctx->out, "{ \"id\":%u, " "\"type\":\"weston_output\", \"name\":", o->timeline.id); fprint_quoted_string(ctx->out, o->name); fprintf(ctx->out, " }\n"); } fprintf(ctx->cur, "\"wo\":%u", o->timeline.id); return 1; } static void check_weston_surface_description(struct timeline_emit_context *ctx, struct weston_surface *s) { struct weston_surface *mains; char d[512]; char mainstr[32]; if (!check_series(ctx, &s->timeline)) return; mains = weston_surface_get_main_surface(s); if (mains != s) { check_weston_surface_description(ctx, mains); if (snprintf(mainstr, sizeof(mainstr), ", \"main_surface\":%u", mains->timeline.id) < 0) mainstr[0] = '\0'; } else { mainstr[0] = '\0'; } if (!s->get_label || s->get_label(s, d, sizeof(d)) < 0) d[0] = '\0'; fprintf(ctx->out, "{ \"id\":%u, " "\"type\":\"weston_surface\", \"desc\":", s->timeline.id); fprint_quoted_string(ctx->out, d[0] ? d : NULL); fprintf(ctx->out, "%s }\n", mainstr); } static int emit_weston_surface(struct timeline_emit_context *ctx, void *obj) { struct weston_surface *s = obj; check_weston_surface_description(ctx, s); fprintf(ctx->cur, "\"ws\":%u", s->timeline.id); return 1; } static int emit_vblank_timestamp(struct timeline_emit_context *ctx, void *obj) { struct timespec *ts = obj; fprintf(ctx->cur, "\"vblank\":[%" PRId64 ", %ld]", (int64_t)ts->tv_sec, ts->tv_nsec); return 1; } static int emit_gpu_timestamp(struct timeline_emit_context *ctx, void *obj) { struct timespec *ts = obj; fprintf(ctx->cur, "\"gpu\":[%" PRId64 ", %ld]", (int64_t)ts->tv_sec, ts->tv_nsec); return 1; } typedef int (*type_func)(struct timeline_emit_context *ctx, void *obj); static const type_func type_dispatch[] = { [TLT_OUTPUT] = emit_weston_output, [TLT_SURFACE] = emit_weston_surface, [TLT_VBLANK] = emit_vblank_timestamp, [TLT_GPU] = emit_gpu_timestamp, }; WL_EXPORT void weston_timeline_point(const char *name, ...) { va_list argp; struct timespec ts; enum timeline_type otype; void *obj; char buf[512]; struct timeline_emit_context ctx; clock_gettime(timeline_.clk_id, &ts); ctx.out = timeline_.file; ctx.cur = fmemopen(buf, sizeof(buf), "w"); ctx.series = timeline_.series; if (!ctx.cur) { weston_log("Timeline error in fmemopen, closing.\n"); weston_timeline_close(); return; } fprintf(ctx.cur, "{ \"T\":[%" PRId64 ", %ld], \"N\":\"%s\"", (int64_t)ts.tv_sec, ts.tv_nsec, name); va_start(argp, name); while (1) { otype = va_arg(argp, enum timeline_type); if (otype == TLT_END) break; obj = va_arg(argp, void *); if (type_dispatch[otype]) { fprintf(ctx.cur, ", "); type_dispatch[otype](&ctx, obj); } } va_end(argp); fprintf(ctx.cur, " }\n"); fflush(ctx.cur); if (ferror(ctx.cur)) { weston_log("Timeline error in constructing entry, closing.\n"); weston_timeline_close(); } else { fprintf(ctx.out, "%s", buf); } fclose(ctx.cur); }