@@ -62,6 +62,9 @@ weston_SOURCES = \
src/noop-renderer.c \
src/pixman-renderer.c \
src/pixman-renderer.h \
+ src/timeline.c \
+ src/timeline.h \
+ src/timeline-object.h \
shared/matrix.c \
shared/matrix.h \
shared/zalloc.h \
@@ -150,6 +153,7 @@ westonincludedir = $(includedir)/weston
westoninclude_HEADERS = \
src/version.h \
src/compositor.h \
+ src/timeline-object.h \
shared/matrix.h \
shared/config-parser.h \
shared/zalloc.h
@@ -2246,6 +2246,14 @@ set_title(struct shell_surface *shsurf, const char *title)
{
free(shsurf->title);
shsurf->title = strdup(title);
+ shsurf->surface->timeline.force_refresh = 1;
+}
+
+static void
+set_type(struct shell_surface *shsurf, enum shell_surface_type t)
+{
+ shsurf->type = t;
+ shsurf->surface->timeline.force_refresh = 1;
}
static void
@@ -2276,6 +2284,7 @@ shell_surface_set_class(struct wl_client *client,
free(shsurf->class);
shsurf->class = strdup(class);
+ shsurf->surface->timeline.force_refresh = 1;
}
static void
@@ -2442,7 +2451,7 @@ set_toplevel(struct shell_surface *shsurf)
{
shell_surface_set_parent(shsurf, NULL);
surface_clear_next_states(shsurf);
- shsurf->type = SHELL_SURFACE_TOPLEVEL;
+ set_type(shsurf, SHELL_SURFACE_TOPLEVEL);
/* The layer_link is updated in set_surface_type(),
* called from configure. */
@@ -2473,7 +2482,7 @@ set_transient(struct shell_surface *shsurf,
shsurf->next_state.relative = true;
shsurf->state_changed = true;
- shsurf->type = SHELL_SURFACE_TOPLEVEL;
+ set_type(shsurf, SHELL_SURFACE_TOPLEVEL);
/* The layer_link is updated in set_surface_type(),
* called from configure. */
@@ -2499,7 +2508,7 @@ set_fullscreen(struct shell_surface *shsurf,
struct weston_output *output)
{
shell_surface_set_output(shsurf, output);
- shsurf->type = SHELL_SURFACE_TOPLEVEL;
+ set_type(shsurf, SHELL_SURFACE_TOPLEVEL);
shsurf->fullscreen_output = shsurf->output;
shsurf->fullscreen.type = method;
@@ -2584,7 +2593,7 @@ set_popup(struct shell_surface *shsurf,
shsurf->popup.x = x;
shsurf->popup.y = y;
- shsurf->type = SHELL_SURFACE_POPUP;
+ set_type(shsurf, SHELL_SURFACE_POPUP);
}
static void
@@ -2688,7 +2697,7 @@ shell_surface_set_maximized(struct wl_client *client,
shsurf->next_state.maximized = true;
shsurf->state_changed = true;
- shsurf->type = SHELL_SURFACE_TOPLEVEL;
+ set_type(shsurf, SHELL_SURFACE_TOPLEVEL);
shell_surface_set_parent(shsurf, NULL);
if (output_resource)
@@ -2977,7 +2986,7 @@ set_xwayland(struct shell_surface *shsurf, int x, int y, uint32_t flags)
shell_surface_set_parent(shsurf, NULL);
- shsurf->type = SHELL_SURFACE_XWAYLAND;
+ set_type(shsurf, SHELL_SURFACE_XWAYLAND);
shsurf->state_changed = true;
}
@@ -3579,7 +3588,7 @@ create_common_surface(struct shell_client *owner, void *shell,
wl_list_init(&shsurf->children_list);
shsurf->parent = NULL;
- shsurf->type = SHELL_SURFACE_NONE;
+ set_type(shsurf, SHELL_SURFACE_NONE);
shsurf->client = client;
@@ -3706,6 +3715,7 @@ xdg_surface_set_app_id(struct wl_client *client,
free(shsurf->class);
shsurf->class = strdup(app_id);
+ shsurf->surface->timeline.force_refresh = 1;
}
static void
@@ -3935,7 +3945,7 @@ create_xdg_surface(struct shell_client *owner, void *shell,
if (!shsurf)
return NULL;
- shsurf->type = SHELL_SURFACE_TOPLEVEL;
+ set_type(shsurf, SHELL_SURFACE_TOPLEVEL);
return shsurf;
}
@@ -4017,7 +4027,7 @@ create_xdg_popup(struct shell_client *owner, void *shell,
if (!shsurf)
return NULL;
- shsurf->type = SHELL_SURFACE_POPUP;
+ set_type(shsurf, SHELL_SURFACE_POPUP);
shsurf->popup.shseat = seat;
shsurf->popup.serial = serial;
shsurf->popup.x = x;
@@ -53,6 +53,8 @@
#include <libunwind.h>
#endif
+#include "timeline.h"
+
#include "compositor.h"
#include "scaler-server-protocol.h"
#include "presentation_timing-server-protocol.h"
@@ -1790,6 +1792,11 @@ surface_flush_damage(struct weston_surface *surface)
wl_shm_buffer_get(surface->buffer_ref.buffer->resource))
surface->compositor->renderer->flush_damage(surface);
+ if (weston_timeline_enabled_ &&
+ pixman_region32_not_empty(&surface->damage))
+ TL_POINT("core_flush_damage", TLP_SURFACE(surface),
+ TLP_OUTPUT(surface->output), TLP_END);
+
pixman_region32_clear(&surface->damage);
}
@@ -2014,6 +2021,8 @@ weston_output_repaint(struct weston_output *output)
if (output->destroying)
return 0;
+ TL_POINT("core_repaint_begin", TLP_OUTPUT(output), TLP_END);
+
/* Rebuild the surface list and update surface transforms up front. */
weston_compositor_build_view_list(ec);
@@ -2069,6 +2078,8 @@ weston_output_repaint(struct weston_output *output)
animation->frame(animation, output, output->frame_time);
}
+ TL_POINT("core_repaint_posted", TLP_OUTPUT(output), TLP_END);
+
return r;
}
@@ -2092,6 +2103,9 @@ weston_output_finish_frame(struct weston_output *output,
int fd, r;
uint32_t refresh_nsec;
+ TL_POINT("core_repaint_finished", TLP_OUTPUT(output),
+ TLP_VBLANK(stamp), TLP_END);
+
refresh_nsec = 1000000000000UL / output->current_mode->refresh;
weston_presentation_feedback_present_list(&output->feedback_list,
output, refresh_nsec, stamp,
@@ -2108,6 +2122,8 @@ weston_output_finish_frame(struct weston_output *output,
}
output->repaint_scheduled = 0;
+ TL_POINT("core_repaint_exit_loop", TLP_OUTPUT(output), TLP_END);
+
if (compositor->input_loop_source)
return;
@@ -2184,6 +2200,9 @@ weston_output_schedule_repaint(struct weston_output *output)
compositor->state == WESTON_COMPOSITOR_OFFSCREEN)
return;
+ if (!output->repaint_needed)
+ TL_POINT("core_repaint_req", TLP_OUTPUT(output), TLP_END);
+
loop = wl_display_get_event_loop(compositor->wl_display);
output->repaint_needed = 1;
if (output->repaint_scheduled)
@@ -2191,6 +2210,8 @@ weston_output_schedule_repaint(struct weston_output *output)
wl_event_loop_add_idle(loop, idle_repaint, output);
output->repaint_scheduled = 1;
+ TL_POINT("core_repaint_enter_loop", TLP_OUTPUT(output), TLP_END);
+
if (compositor->input_loop_source) {
wl_event_source_remove(compositor->input_loop_source);
@@ -2362,6 +2383,9 @@ weston_surface_commit_state(struct weston_surface *surface,
state->buffer_viewport.changed = 0;
/* wl_surface.damage */
+ if (weston_timeline_enabled_ &&
+ pixman_region32_not_empty(&state->damage))
+ TL_POINT("core_commit_damage", TLP_SURFACE(surface), TLP_END);
pixman_region32_union(&surface->damage, &surface->damage,
&state->damage);
pixman_region32_intersect_rect(&surface->damage, &surface->damage,
@@ -2832,6 +2856,7 @@ weston_surface_set_description(struct weston_surface *surface,
char *, size_t))
{
surface->get_description = desc;
+ surface->timeline.force_refresh = 1;
}
static void
@@ -4037,6 +4062,18 @@ weston_environment_get_fd(const char *env)
return fd;
}
+static void
+timeline_key_binding_handler(struct weston_seat *seat, uint32_t time,
+ uint32_t key, void *data)
+{
+ struct weston_compositor *compositor = data;
+
+ if (weston_timeline_enabled_)
+ weston_timeline_close();
+ else
+ weston_timeline_open(compositor);
+}
+
WL_EXPORT int
weston_compositor_init(struct weston_compositor *ec,
struct wl_display *display,
@@ -4134,6 +4171,9 @@ weston_compositor_init(struct weston_compositor *ec,
weston_layer_init(&ec->fade_layer, &ec->layer_list);
weston_layer_init(&ec->cursor_layer, &ec->fade_layer.link);
+ weston_compositor_add_debug_binding(ec, KEY_T,
+ timeline_key_binding_handler, ec);
+
weston_compositor_schedule_repaint(ec);
return 0;
@@ -40,6 +40,7 @@ extern "C" {
#include "matrix.h"
#include "config-parser.h"
#include "zalloc.h"
+#include "timeline-object.h"
#ifndef MIN
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
@@ -234,6 +235,8 @@ struct weston_output {
uint16_t *r,
uint16_t *g,
uint16_t *b);
+
+ struct weston_timeline_object timeline;
};
struct weston_pointer_grab;
@@ -916,6 +919,8 @@ struct weston_surface {
* and replace role_name with configure.
*/
const char *role_name;
+
+ struct weston_timeline_object timeline;
};
struct weston_subsurface {
new file mode 100644
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
+ * Copyright © 2014 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WESTON_TIMELINE_OBJECT_H
+#define WESTON_TIMELINE_OBJECT_H
+
+/*
+ * This struct can be embedded in objects related to timeline output.
+ * It must be initialized to all-zero. Afterwards, the timeline code
+ * will handle it alone. No clean-up is necessary.
+ */
+struct weston_timeline_object {
+ /*
+ * Timeline series gets bumped every time a new log is opened.
+ * This triggers id allocation and object info emission.
+ * 0 is an invalid series value.
+ */
+ unsigned series;
+
+ /* Object id in the timeline JSON output. 0 is invalid. */
+ unsigned id;
+
+ /*
+ * If non-zero, forces a re-emission of object description.
+ * Should be set to non-zero, when changing long-lived
+ * object state that is not emitted on normal timeline
+ * events.
+ */
+ unsigned force_refresh;
+};
+
+#endif /* WESTON_TIMELINE_OBJECT_H */
new file mode 100644
@@ -0,0 +1,287 @@
+/*
+ * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
+ * Copyright © 2014 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+
+#include "timeline.h"
+#include "compositor.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)
+{
+ time_t t;
+ struct tm *tmp;
+ char fname[1000];
+ int ret;
+
+ t = time(NULL);
+ tmp = localtime(&t);
+ if (!tmp) {
+ weston_log("Conversion to local time failed, "
+ "cannot open timeline log file.\n");
+ return -1;
+ }
+
+ ret = strftime(fname, sizeof(fname),
+ "weston-timeline-%F_%H-%M-%S.log", tmp);
+ if (ret == 0) {
+ weston_log("Time formatting failed, "
+ "cannot open timeline log file.\n");
+ return -1;
+ }
+
+ timeline_.file = fopen(fname, "w");
+ if (!timeline_.file) {
+ weston_log("Cannot open '%s' for writing: %s\n",
+ fname, strerror(errno));
+ 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 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\":%s%s%s }\n",
+ o->timeline.id,
+ o->name ? "\"" : "", o->name ?: "null",
+ o->name ? "\"" : "");
+
+ 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;
+ const char *q = "\"";
+ 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_description ||
+ s->get_description(s, d, sizeof(d)) < 0) {
+ d[0] = '\0';
+ q = "";
+ }
+
+ fprintf(ctx->out, "{ \"id\":%u, "
+ "\"type\":\"weston_surface\", "
+ "\"desc\":%s%s%s%s }\n",
+ s->timeline.id, q, d[0] ? d : "null", q, 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;
+}
+
+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,
+};
+
+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);
+}
new file mode 100644
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2014 Pekka Paalanen <pq@iki.fi>
+ * Copyright © 2014 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WESTON_TIMELINE_H
+#define WESTON_TIMELINE_H
+
+extern int weston_timeline_enabled_;
+
+struct weston_compositor;
+
+void
+weston_timeline_open(struct weston_compositor *compositor);
+
+void
+weston_timeline_close(void);
+
+enum timeline_type {
+ TLT_END = 0,
+ TLT_OUTPUT,
+ TLT_SURFACE,
+ TLT_VBLANK,
+};
+
+#define TYPEVERIFY(type, arg) ({ \
+ typeof(arg) tmp___ = (arg); \
+ (void)((type)0 == tmp___); \
+ tmp___; })
+
+#define TLP_END TLT_END, NULL
+#define TLP_OUTPUT(o) TLT_OUTPUT, TYPEVERIFY(struct weston_output *, (o))
+#define TLP_SURFACE(s) TLT_SURFACE, TYPEVERIFY(struct weston_surface *, (s))
+#define TLP_VBLANK(t) TLT_VBLANK, TYPEVERIFY(const struct timespec *, (t))
+
+#define TL_POINT(...) do { \
+ if (weston_timeline_enabled_) \
+ weston_timeline_point(__VA_ARGS__); \
+} while (0)
+
+void
+weston_timeline_point(const char *name, ...);
+
+#endif /* WESTON_TIMELINE_H */