diff --git a/include/xwayland/trace.h b/include/xwayland/trace.h new file mode 100644 index 000000000..aeaee4d32 --- /dev/null +++ b/include/xwayland/trace.h @@ -0,0 +1,23 @@ +#ifndef XWAYLAND_TRACE_H +#define XWAYLAND_TRACE_H + +#include +#include + +struct wlr_x11_trace { + struct wl_list trace_events; // trace_event::link +}; + +void wlr_x11_trace_init(struct wlr_x11_trace *trace); +void wlr_x11_trace_deinit(struct wlr_x11_trace *trace); + +void wlr_x11_trace_received_event(struct wlr_x11_trace *trace, xcb_generic_event_t *event); +void wlr_x11_trace_log_error_trace(struct wlr_x11_trace *trace, uint32_t sequence); + +void wlr_x11_trace_trace(struct wlr_x11_trace *trace, unsigned int sequence, + const char *file, unsigned int line); + +#define WLR_X11_TRACE(trace, conn) \ + wlr_x11_trace_trace((trace), xcb_no_operation(conn).sequence, __FILE__, __LINE__) + +#endif diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 362b4cef0..ac250b22c 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -12,6 +12,7 @@ #include #endif #include "xwayland/selection.h" +#include "xwayland/trace.h" /* This is in xcb/xcb_event.h, but pulling xcb-util just for a constant * others redefine anyway is meh @@ -98,6 +99,7 @@ struct wlr_xwm { struct wlr_xwayland *xwayland; struct wl_event_source *event_source; struct wlr_seat *seat; + struct wlr_x11_trace trace; uint32_t ping_timeout; xcb_atom_t atoms[ATOM_LAST]; diff --git a/xwayland/meson.build b/xwayland/meson.build index 6cd771898..b5648f24e 100644 --- a/xwayland/meson.build +++ b/xwayland/meson.build @@ -56,6 +56,7 @@ wlr_files += files( 'selection/selection.c', 'server.c', 'sockets.c', + 'trace.c', 'xwayland.c', 'xwm.c', ) diff --git a/xwayland/trace.c b/xwayland/trace.c new file mode 100644 index 000000000..6d8df4895 --- /dev/null +++ b/xwayland/trace.c @@ -0,0 +1,77 @@ +#include "xwayland/trace.h" +#include +#include + +struct trace_event { + const char *file; + unsigned int line; + unsigned int sequence; + struct wl_list link; +}; + +static void trace_event_free(struct trace_event *event) { + wl_list_remove(&event->link); + free(event); +} + +void wlr_x11_trace_init(struct wlr_x11_trace *trace) { + wl_list_init(&trace->trace_events); +} + +void wlr_x11_trace_deinit(struct wlr_x11_trace *trace) { + struct trace_event *event, *next; + wl_list_for_each_safe(event, next, &trace->trace_events, link) { + trace_event_free(event); + } +} + +void wlr_x11_trace_trace(struct wlr_x11_trace *trace, unsigned int sequence, + const char *file, unsigned int line) { + struct trace_event *event = malloc(sizeof(*event)); + if (event == NULL) { + return; + } + + event->file = file; + event->line = line; + event->sequence = sequence; + + wl_list_insert(trace->trace_events.prev, &event->link); +} + +// Remove entries from the event queue which are no longer needed. +// +// This removes all but one event with an older sequence number than the given +// event. +void wlr_x11_trace_received_event(struct wlr_x11_trace *trace, xcb_generic_event_t *event) { + struct trace_event *entry, *next, *prev = NULL; + wl_list_for_each_safe(entry, next, &trace->trace_events, link) { + if (prev) { + if (entry->sequence >= event->full_sequence) { + break; + } + trace_event_free(prev); + } + prev = entry; + } +} + +void wlr_x11_trace_log_error_trace(struct wlr_x11_trace *trace, uint32_t sequence) { + struct trace_event *entry, *prev = NULL; + wl_list_for_each(entry, &trace->trace_events, link) { + if (entry->sequence >= sequence) { + if (prev) { + wlr_log(WLR_ERROR, "X11 error happened somewhere after %s:%d", + prev->file, prev->line); + } + wlr_log(WLR_ERROR, "X11 error happened somewhere before %s:%d", + entry->file, entry->line); + return; + } + prev = entry; + } + if (prev) { + wlr_log(WLR_ERROR, "X11 error happened somewhere after %s:%d", + prev->file, prev->line); + } +} diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 522b72554..d8d333dd2 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1399,6 +1399,7 @@ static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_generic_error_t *ev) { major_name, minor_name ? minor_name : "no minor", error_name, extension ? extension : "no extension", ev->sequence, ev->resource_id); + wlr_x11_trace_log_error_trace(&xwm->trace, ev->full_sequence); return; log_raw: @@ -1407,6 +1408,7 @@ log_raw: "xcb error: op %"PRIu8":%"PRIu16", code %"PRIu8", sequence %"PRIu16", value %"PRIu32, ev->major_code, ev->minor_code, ev->error_code, ev->sequence, ev->resource_id); + wlr_x11_trace_log_error_trace(&xwm->trace, ev->full_sequence); } @@ -1441,6 +1443,8 @@ static int x11_event_handler(int fd, uint32_t mask, void *data) { while ((event = xcb_poll_for_event(xwm->xcb_conn))) { count++; + wlr_x11_trace_received_event(&xwm->trace, event); + if (xwm->xwayland->user_event_handler && xwm->xwayland->user_event_handler(xwm, event)) { break; @@ -1634,6 +1638,7 @@ void xwm_destroy(struct wlr_xwm *xwm) { } wl_list_remove(&xwm->compositor_new_surface.link); wl_list_remove(&xwm->compositor_destroy.link); + wlr_x11_trace_deinit(&xwm->trace); xcb_disconnect(xwm->xcb_conn); xwm->xwayland->xwm = NULL; @@ -1850,6 +1855,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->xwayland = xwayland; wl_list_init(&xwm->surfaces); wl_list_init(&xwm->unpaired_surfaces); + wlr_x11_trace_init(&xwm->trace); xwm->ping_timeout = 10000; xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL);