Skip to content

Commit

Permalink
x: print more context for X errors
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
yshui committed Jun 24, 2024
1 parent e0d4746 commit 396f72a
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 138 deletions.
243 changes: 124 additions & 119 deletions src/x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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:
Expand All @@ -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.
Expand Down Expand Up @@ -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
*
Expand All @@ -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);
}

/**
Expand Down
47 changes: 28 additions & 19 deletions src/x.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 396f72a

Please sign in to comment.