From 744fa2e1b701658be688bdcb761ecc914b6b1a21 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sat, 9 Jan 2021 13:11:53 +0100 Subject: [PATCH] xwm: Add an X11 error tracing infrastructure This adds some infrastructure that records the current X11 sequence number, file name, and line number at various places. When an X11 error later comes in, this tracing infrastructure can use the sequence number of the error to figure out which piece of code caused this X11 error. Signed-off-by: Uli Schlachter --- include/xwayland/trace.h | 23 ++++++++++++ include/xwayland/xwm.h | 2 ++ xwayland/meson.build | 1 + xwayland/trace.c | 77 ++++++++++++++++++++++++++++++++++++++++ xwayland/xwm.c | 6 ++++ 5 files changed, 109 insertions(+) create mode 100644 include/xwayland/trace.h create mode 100644 xwayland/trace.c 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);