1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-04-07 17:44:04 -04:00

x: print more context for X errors

For x_print_error, print the name of the caller instead of
"x_print_error".

For registered x error actions, print where the request was originally
made.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-06-24 11:22:12 +01:00
parent 2c6560033c
commit 23563c1ac8
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
2 changed files with 152 additions and 138 deletions

243
src/x.c
View file

@ -60,6 +60,114 @@ void x_discard_pending_errors(struct x_connection *c, uint32_t sequence) {
}
}
enum {
XSyncBadCounter = 0,
XSyncBadAlarm = 1,
XSyncBadFence = 2,
};
/// Convert a X11 error to string
///
/// @return a pointer to a string. this pointer shouldn NOT be freed, same buffer is used
/// for multiple calls to this function,
static const char *x_error_code_to_string(unsigned long serial, uint8_t major,
uint16_t minor, uint8_t error_code) {
session_t *const ps = ps_g;
int o = 0;
const char *name = "Unknown";
#define CASESTRRET(s) \
case s: name = #s; break
#define CASESTRRET2(s) \
case XCB_##s: name = #s; break
// TODO(yshui) separate error code out from session_t
o = error_code - ps->xfixes_error;
switch (o) { CASESTRRET2(XFIXES_BAD_REGION); }
o = error_code - ps->damage_error;
switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); }
o = error_code - ps->render_error;
switch (o) {
CASESTRRET2(RENDER_PICT_FORMAT);
CASESTRRET2(RENDER_PICTURE);
CASESTRRET2(RENDER_PICT_OP);
CASESTRRET2(RENDER_GLYPH_SET);
CASESTRRET2(RENDER_GLYPH);
}
if (ps->glx_exists) {
o = error_code - ps->glx_error;
switch (o) {
CASESTRRET2(GLX_BAD_CONTEXT);
CASESTRRET2(GLX_BAD_CONTEXT_STATE);
CASESTRRET2(GLX_BAD_DRAWABLE);
CASESTRRET2(GLX_BAD_PIXMAP);
CASESTRRET2(GLX_BAD_CONTEXT_TAG);
CASESTRRET2(GLX_BAD_CURRENT_WINDOW);
CASESTRRET2(GLX_BAD_RENDER_REQUEST);
CASESTRRET2(GLX_BAD_LARGE_REQUEST);
CASESTRRET2(GLX_UNSUPPORTED_PRIVATE_REQUEST);
CASESTRRET2(GLX_BAD_FB_CONFIG);
CASESTRRET2(GLX_BAD_PBUFFER);
CASESTRRET2(GLX_BAD_CURRENT_DRAWABLE);
CASESTRRET2(GLX_BAD_WINDOW);
CASESTRRET2(GLX_GLX_BAD_PROFILE_ARB);
}
}
if (ps->xsync_exists) {
o = error_code - ps->xsync_error;
switch (o) {
CASESTRRET(XSyncBadCounter);
CASESTRRET(XSyncBadAlarm);
CASESTRRET(XSyncBadFence);
}
}
switch (error_code) {
CASESTRRET2(ACCESS);
CASESTRRET2(ALLOC);
CASESTRRET2(ATOM);
CASESTRRET2(COLORMAP);
CASESTRRET2(CURSOR);
CASESTRRET2(DRAWABLE);
CASESTRRET2(FONT);
CASESTRRET2(G_CONTEXT);
CASESTRRET2(ID_CHOICE);
CASESTRRET2(IMPLEMENTATION);
CASESTRRET2(LENGTH);
CASESTRRET2(MATCH);
CASESTRRET2(NAME);
CASESTRRET2(PIXMAP);
CASESTRRET2(REQUEST);
CASESTRRET2(VALUE);
CASESTRRET2(WINDOW);
}
#undef CASESTRRET
#undef CASESTRRET2
thread_local static char buffer[256];
snprintf(buffer, sizeof(buffer), "X error %d %s request %d minor %d serial %lu",
error_code, name, major, minor, serial);
return buffer;
}
void x_print_error_impl(unsigned long serial, uint8_t major, uint16_t minor,
uint8_t error_code, const char *func) {
if (unlikely(LOG_LEVEL_DEBUG >= log_get_level_tls())) {
log_printf(tls_logger, LOG_LEVEL_DEBUG, func, "%s",
x_error_code_to_string(serial, major, minor, error_code));
}
}
/// Handle X errors.
///
/// This function logs X errors, or aborts the program based on severity of the error.
void x_handle_error(struct x_connection *c, xcb_generic_error_t *ev) {
x_discard_pending_errors(c, ev->full_sequence);
struct pending_x_error *first_error_action = NULL;
@ -69,8 +177,17 @@ void x_handle_error(struct x_connection *c, xcb_generic_error_t *ev) {
}
if (first_error_action != NULL && first_error_action->sequence == ev->full_sequence) {
if (first_error_action->action != PENDING_REPLY_ACTION_IGNORE) {
x_log_error(LOG_LEVEL_ERROR, ev->full_sequence, ev->major_code,
ev->minor_code, ev->error_code);
log_error("X error for request in %s at %s:%d: %s",
first_error_action->func, first_error_action->file,
first_error_action->line,
x_error_code_to_string(ev->full_sequence, ev->major_code,
ev->minor_code, ev->error_code));
} else {
log_debug("Expected X error for request in %s at %s:%d: %s",
first_error_action->func, first_error_action->file,
first_error_action->line,
x_error_code_to_string(ev->full_sequence, ev->major_code,
ev->minor_code, ev->error_code));
}
switch (first_error_action->action) {
case PENDING_REPLY_ACTION_ABORT:
@ -82,8 +199,9 @@ void x_handle_error(struct x_connection *c, xcb_generic_error_t *ev) {
}
return;
}
x_log_error(LOG_LEVEL_WARN, ev->full_sequence, ev->major_code, ev->minor_code,
ev->error_code);
log_warn("Stray X error: %s",
x_error_code_to_string(ev->full_sequence, ev->major_code, ev->minor_code,
ev->error_code));
}
/// Initialize x_connection struct from an Xlib Display.
@ -565,120 +683,6 @@ void x_free_picture(struct x_connection *c, xcb_render_picture_t p) {
x_set_error_action_debug_abort(c, cookie);
}
enum {
XSyncBadCounter = 0,
XSyncBadAlarm = 1,
XSyncBadFence = 2,
};
/**
* Convert a X11 error to string
*
* @return a pointer to a string. this pointer shouldn NOT be freed, same buffer is used
* for multiple calls to this function,
*/
static const char *
_x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) {
session_t *const ps = ps_g;
int o = 0;
const char *name = "Unknown";
#define CASESTRRET(s) \
case s: name = #s; break
#define CASESTRRET2(s) \
case XCB_##s: name = #s; break
// TODO(yshui) separate error code out from session_t
o = error_code - ps->xfixes_error;
switch (o) { CASESTRRET2(XFIXES_BAD_REGION); }
o = error_code - ps->damage_error;
switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); }
o = error_code - ps->render_error;
switch (o) {
CASESTRRET2(RENDER_PICT_FORMAT);
CASESTRRET2(RENDER_PICTURE);
CASESTRRET2(RENDER_PICT_OP);
CASESTRRET2(RENDER_GLYPH_SET);
CASESTRRET2(RENDER_GLYPH);
}
if (ps->glx_exists) {
o = error_code - ps->glx_error;
switch (o) {
CASESTRRET2(GLX_BAD_CONTEXT);
CASESTRRET2(GLX_BAD_CONTEXT_STATE);
CASESTRRET2(GLX_BAD_DRAWABLE);
CASESTRRET2(GLX_BAD_PIXMAP);
CASESTRRET2(GLX_BAD_CONTEXT_TAG);
CASESTRRET2(GLX_BAD_CURRENT_WINDOW);
CASESTRRET2(GLX_BAD_RENDER_REQUEST);
CASESTRRET2(GLX_BAD_LARGE_REQUEST);
CASESTRRET2(GLX_UNSUPPORTED_PRIVATE_REQUEST);
CASESTRRET2(GLX_BAD_FB_CONFIG);
CASESTRRET2(GLX_BAD_PBUFFER);
CASESTRRET2(GLX_BAD_CURRENT_DRAWABLE);
CASESTRRET2(GLX_BAD_WINDOW);
CASESTRRET2(GLX_GLX_BAD_PROFILE_ARB);
}
}
if (ps->xsync_exists) {
o = error_code - ps->xsync_error;
switch (o) {
CASESTRRET(XSyncBadCounter);
CASESTRRET(XSyncBadAlarm);
CASESTRRET(XSyncBadFence);
}
}
switch (error_code) {
CASESTRRET2(ACCESS);
CASESTRRET2(ALLOC);
CASESTRRET2(ATOM);
CASESTRRET2(COLORMAP);
CASESTRRET2(CURSOR);
CASESTRRET2(DRAWABLE);
CASESTRRET2(FONT);
CASESTRRET2(G_CONTEXT);
CASESTRRET2(ID_CHOICE);
CASESTRRET2(IMPLEMENTATION);
CASESTRRET2(LENGTH);
CASESTRRET2(MATCH);
CASESTRRET2(NAME);
CASESTRRET2(PIXMAP);
CASESTRRET2(REQUEST);
CASESTRRET2(VALUE);
CASESTRRET2(WINDOW);
}
#undef CASESTRRET
#undef CASESTRRET2
thread_local static char buffer[256];
snprintf(buffer, sizeof(buffer), "X error %d %s request %d minor %d serial %lu",
error_code, name, major, minor, serial);
return buffer;
}
/**
* Log a X11 error
*/
void x_log_error(enum log_level level, unsigned long serial, uint8_t major,
uint16_t minor, uint8_t error_code) {
if (unlikely(level >= log_get_level_tls())) {
log_printf(tls_logger, level, __func__, "%s",
_x_strerror(serial, major, minor, error_code));
}
}
void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) {
x_log_error(LOG_LEVEL_DEBUG, serial, major, minor, error_code);
}
/*
* Convert a xcb_generic_error_t to a string that describes the error
*
@ -689,7 +693,8 @@ const char *x_strerror(xcb_generic_error_t *e) {
if (!e) {
return "No error";
}
return _x_strerror(e->full_sequence, e->major_code, e->minor_code, e->error_code);
return x_error_code_to_string(e->full_sequence, e->major_code, e->minor_code,
e->error_code);
}
/**

47
src/x.h
View file

@ -55,6 +55,12 @@ enum x_error_action {
struct pending_x_error {
unsigned long sequence;
enum x_error_action action;
// Debug information, where in the code was this request sent.
const char *func;
const char *file;
int line;
struct list_node siblings;
};
@ -137,38 +143,40 @@ static inline uint32_t x_new_id(struct x_connection *c) {
/// @param c X connection
/// @param sequence sequence number of the X request to set error handler for
/// @param action action to take when error occurs
static void
x_set_error_action(struct x_connection *c, uint32_t sequence, enum x_error_action action) {
static inline void
x_set_error_action(struct x_connection *c, uint32_t sequence, enum x_error_action action,
const char *func, const char *file, int line) {
auto i = cmalloc(struct pending_x_error);
i->sequence = sequence;
i->action = action;
i->func = func;
i->file = file;
i->line = line;
list_insert_before(&c->pending_x_errors, &i->siblings);
}
/// Convenience wrapper for x_set_error_action with action `PENDING_REPLY_ACTION_IGNORE`
static inline void attr_unused x_set_error_action_ignore(struct x_connection *c,
xcb_void_cookie_t cookie) {
x_set_error_action(c, cookie.sequence, PENDING_REPLY_ACTION_IGNORE);
}
#define x_set_error_action_ignore(c, cookie) \
x_set_error_action(c, (cookie).sequence, PENDING_REPLY_ACTION_IGNORE, __func__, \
__FILE__, __LINE__)
/// Convenience wrapper for x_set_error_action with action `PENDING_REPLY_ACTION_ABORT`
static inline void attr_unused x_set_error_action_abort(struct x_connection *c,
xcb_void_cookie_t cookie) {
x_set_error_action(c, cookie.sequence, PENDING_REPLY_ACTION_ABORT);
}
#define x_set_error_action_abort(c, cookie) \
x_set_error_action(c, (cookie).sequence, PENDING_REPLY_ACTION_ABORT, __func__, \
__FILE__, __LINE__)
/// Convenience wrapper for x_set_error_action with action
/// `PENDING_REPLY_ACTION_DEBUG_ABORT`
static inline void attr_unused x_set_error_action_debug_abort(struct x_connection *c,
xcb_void_cookie_t cookie) {
#ifndef NDEBUG
x_set_error_action(c, cookie.sequence, PENDING_REPLY_ACTION_DEBUG_ABORT);
#define x_set_error_action_debug_abort(c, cookie) \
x_set_error_action(c, (cookie).sequence, PENDING_REPLY_ACTION_DEBUG_ABORT, \
__func__, __FILE__, __LINE__)
#else
(void)c;
(void)cookie;
#define x_set_error_action_debug_abort(c, cookie) \
((void)(c)); \
((void)(cookie))
#endif
}
static inline void attr_unused free_x_connection(struct x_connection *c) {
list_foreach_safe(struct pending_x_error, i, &c->pending_x_errors, siblings) {
@ -327,9 +335,10 @@ void x_free_picture(struct x_connection *c, xcb_render_picture_t p);
/**
* Log a X11 error
*/
void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code);
void x_log_error(enum log_level level, unsigned long serial, uint8_t major,
uint16_t minor, uint8_t error_code);
void x_print_error_impl(unsigned long serial, uint8_t major, uint16_t minor,
uint8_t error_code, const char *func);
#define x_print_error(serial, major, minor, error_code) \
x_print_error_impl(serial, major, minor, error_code, __func__)
/*
* Convert a xcb_generic_error_t to a string that describes the error