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

misc: add a xcb patch to help detect freeze conditions

Patch xcb to expose the internal buffer of a xcb_connection_t, so picom can
assert that it's empty before it goes to sleep. With nix its easy to
setup a dev environment that builds libxcb with this patch.

Also rename the `useClangProfile` devShell to `useClangDebuggable` which
I think is a better name. (Pretty sure I am the only person that uses
this anyways).

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2025-03-13 18:06:27 +00:00
parent b99537235b
commit 10ba9600f6
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
4 changed files with 122 additions and 3 deletions

View file

@ -26,9 +26,18 @@
inherit system overlays;
config.allowBroken = true;
};
profilePkgs = import nixpkgs {
debuggablePkgs = import nixpkgs {
inherit system;
overlays = overlays ++ [
(final: prev: {
xorg = prev.xorg.overrideScope (_: xprev: {
libxcb = final.enableDebugging (xprev.libxcb.overrideAttrs (o: {
patches = (o.patches or []) ++ [
./nix/patches/xcb-add-debug-functions.patch
];
}));
});
})
(final: prev: {
stdenv = prev.withCFlags "-fno-omit-frame-pointer" prev.stdenv;
})
@ -75,8 +84,8 @@
};
# build picom and all dependencies with frame pointer, making profiling/debugging easier.
# WARNING! many many rebuilds
devShells.useClangProfile = (mkDevShell (profilePkgs.picom.override { devShell = true; })).override {
stdenv = profilePkgs.withCFlags "-fno-omit-frame-pointer" profilePkgs.llvmPackages_18.stdenv;
devShells.useClangDebuggable = (mkDevShell (debuggablePkgs.picom.override { devShell = true; })).override {
stdenv = debuggablePkgs.withCFlags "-fno-omit-frame-pointer" debuggablePkgs.llvmPackages_18.stdenv;
};
});
}

View file

@ -0,0 +1,82 @@
diff --git i/src/xcb.h w/src/xcb.h
index 8d1b456..f37561d 100644
--- i/src/xcb.h
+++ w/src/xcb.h
@@ -386,6 +386,11 @@ xcb_special_event_t *xcb_register_for_special_xge(xcb_connection_t *c,
void xcb_unregister_for_special_event(xcb_connection_t *c,
xcb_special_event_t *se);
+/**
+ * @brief Returns an arbitrary message from the connection's internal buffer if any.
+ */
+xcb_generic_event_t *xcb_picom_peek_message(xcb_connection_t *c);
+
/**
* @brief Return the error for a request, or NULL if none can ever arrive.
* @param c The connection to the X server.
diff --git i/src/xcb_in.c w/src/xcb_in.c
index 62526f6..7c13022 100644
--- i/src/xcb_in.c
+++ w/src/xcb_in.c
@@ -25,6 +25,7 @@
/* Stuff that reads stuff from the server. */
+#include <stdint.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -913,6 +914,25 @@ xcb_unregister_for_special_event(xcb_connection_t *c,
pthread_mutex_unlock(&c->iolock);
}
+xcb_generic_event_t *xcb_picom_peek_message(xcb_connection_t *c) {
+ if (c->in.events) {
+ return c->in.events->event;
+ }
+ if (c->in.current_reply) {
+ return c->in.current_reply->reply;
+ }
+ if (_xcb_map_head(c->in.replies)) {
+ struct reply_list *list = _xcb_map_head(c->in.replies);
+ return list->reply;
+ }
+ for (xcb_special_event_t *s, **prev = &c->in.special_events; (s = *prev) != NULL; prev = &(s->next)) {
+ if (s->events) {
+ return s->events->event;
+ }
+ }
+ return NULL;
+}
+
/* Private interface */
int _xcb_in_init(_xcb_in *in)
diff --git i/src/xcb_list.c w/src/xcb_list.c
index bdd2d43..9dfaaac 100644
--- i/src/xcb_list.c
+++ w/src/xcb_list.c
@@ -103,3 +103,11 @@ void *_xcb_map_remove(_xcb_map *list, uint64_t key)
}
return 0;
}
+
+void *_xcb_map_head(_xcb_map *list) {
+ if (!list || !list->head) {
+ return NULL;
+ }
+ return list->head->data;
+}
+
diff --git i/src/xcbint.h w/src/xcbint.h
index 9836def..ba2d27c 100644
--- i/src/xcbint.h
+++ w/src/xcbint.h
@@ -85,6 +85,7 @@ _xcb_map *_xcb_map_new(void);
void _xcb_map_delete(_xcb_map *q, xcb_list_free_func_t do_free);
int _xcb_map_put(_xcb_map *q, uint64_t key, void *data);
void *_xcb_map_remove(_xcb_map *q, uint64_t key);
+void *_xcb_map_head(_xcb_map *list);
/* xcb_out.c */

View file

@ -57,6 +57,10 @@ foreach i : required_xcb_packages
base_deps += [dependency(i, version: '>=1.12.0', required: true)]
endforeach
if cc.has_function('xcb_picom_peek_message', prefix: '#include <xcb/xcb.h>', dependencies: base_deps)
cflags += ['-DHAS_XCB_PICOM_DEBUG_FUNCTIONS']
endif
libconfig_dep = dependency('libconfig', version: '>=1.7', required: false)
if not libconfig_dep.found()

View file

@ -1281,6 +1281,28 @@ static void unredirect(session_t *ps) {
log_debug("Screen unredirected.");
}
/// Returns true if there is no queued messages in xcb_connection_t's buffer.
static bool check_xcb_buffer_is_empty(xcb_connection_t *c) {
#ifdef HAS_XCB_PICOM_DEBUG_FUNCTIONS
xcb_generic_event_t *buffered = xcb_picom_peek_message(c);
if (buffered != NULL) {
if (buffered->response_type != 1) {
// not a reply, full_sequence is valid
log_fatal("Going into sleep with messages in connection's "
"buffer, seq: %x",
buffered->full_sequence);
} else {
log_fatal("Going into sleep with replies in connection's buffer, "
"seq: %x",
buffered->sequence);
}
return false;
}
#endif
return true;
}
/// Handle queued events before we go to sleep.
///
/// This function is called by ev_prepare watcher, which is called just before
@ -1333,6 +1355,8 @@ static void handle_x_events(struct session *ps) {
ps->pending_updates = true;
queue_redraw(ps);
}
assert(check_xcb_buffer_is_empty(ps->c.c));
}
static void handle_x_events_ev(EV_P attr_unused, ev_prepare *w, int revents attr_unused) {