An all-in-one developer toolkit for GNOME Shell extension authors — built as a native GTK4 / libadwaita app that fits right into your desktop.
GSE Profiler installs a lightweight bridge extension inside the running shell process and instruments your extension at runtime, with zero changes to your code. Profile function timing and visualise it as a flamegraph (call tree and timing), swimlane (when and how often each function runs), or histogram (where time is actually spent). Filter and search live logs scoped to a single extension UUID. Inspect the live state of any running extension object: browse properties, see current values, and drill into nested objects on the fly.
| Feature | Description |
|---|---|
| Extension Manager | Browse all installed extensions with status, enable/disable with one click, open the source folder directly |
| Log Viewer | Live systemd journal stream scoped to a single extension UUID; journalctl-compatible filter syntax (--user, -t, -u, -b, -p); filter by log level and search full-text in real time |
| Profiler | Monkey-patch any extension at runtime. No code changes needed. Visualise timing as a flamegraph, swimlane, or histogram; export and reload sessions as JSON |
| Inspector | Inspect a live extension object: browse its properties and methods, see current values, and call methods interactively |
GSE Profiler is split into two parts: a GTK4 app (Python) and a bridge GJS extension
(gse-profiler-bridge@todevelopers) that the app auto-installs on first launch. The bridge
runs inside the gnome-shell process itself — giving it direct, in-process access to every
loaded extension's live objects and functions.
GNOME Shell must be restarted once after the bridge is installed: the app prompts you to log out and back in (Wayland-only — X11 sessions are not supported).
The main window shows a live connection indicator so you always know whether the bridge is reachable.
The app and bridge talk over a Unix domain socket
($XDG_RUNTIME_DIR/gse-profiler/gse-profiler.sock) using newline-delimited JSON. The bridge
initiates the connection and reconnects automatically after a failure (3 s delay). On connect
it sends a hello handshake; from that point the app can start a profiling or inspection session.
Standard extension management — listing, enabling, disabling — uses the regular
org.gnome.Shell.Extensions D-Bus interface. The socket exists only for
data that D-Bus is not suited for: high-frequency profiling events and live inspection results.
No elevated permissions are required for either path.
When you start profiling, the bridge walks the extension's stateObj — its full prototype
chain and one level of owned child objects (e.g. _indicator) — and wraps every enumerable
function it finds. No changes to your extension's source are needed; all patches are
fully reversed when you stop.
Each wrapped call adds two GLib.get_monotonic_time() reads (microsecond precision) and
queues one JSON event for the socket. For a typical GNOME Shell extension the overhead is
negligible, but extremely tight animation loops or extensions that invoke hundreds of
functions per frame may see a measurable slowdown during recording.
What the profiler cannot patch:
- GObject virtual functions (vfuncs)
- Closures stored in plain variables (not reachable by property enumeration)
- Functions added dynamically after profiling starts
| Limit | Value |
|---|---|
| Max recorded events | 50,000 (oldest dropped first) |
| Inspector: max properties per object | 50 |
| Inspector: max string value length | 200 characters |
| Inspector: max array elements shown | 50 |
| UI refresh batch window | 80 ms |
All three views work on the same event data — they just answer different questions:
Flamegraph — call tree laid out on a real-time axis. Each bar is one call; width equals duration; nesting shows caller/callee depth. Best for understanding what called what and spotting unexpectedly deep or wide call stacks.
Swimlane — one horizontal lane per function, idle gaps compressed. Each invocation is a separate segment on that function's row, so you can see when and how often each function runs without the call-depth nesting getting in the way.
Histogram — functions ranked by self-time (wall-clock time spent in the function's own code, excluding callees). The fastest answer to where is time actually being spent? — it filters out time that belongs to the callee, not the caller.
Requires GNOME Shell 46+ in an active Wayland GNOME session.
Grab the .flatpak bundle from the
latest release
and install it:
flatpak install --user gse-profiler-*.flatpak
flatpak run io.github.todevelopers.GseProfilercurl -fsSL https://raw.githubusercontent.com/todevelopers/gse-profiler/main/scripts/setup-and-run.sh | bashThe script checks for GTK4 / libadwaita, clones the repository to
~/gse-profiler, and launches the app — no sudo, no prompts. On
subsequent runs the same command pulls the latest changes and restarts
the app.
curl -fsSL https://raw.githubusercontent.com/todevelopers/gse-profiler/main/scripts/uninstall.sh | bashRemoves the app, desktop entry, icon, and bridge extension. Nothing else on your system is touched.
- GNOME Shell 46+ (tested up to 50)
- Python 3.11+
- GTK 4 and libadwaita 1
- PyGObject (GTK4 bindings)
python3-systemd— for the log viewer (systemd.journal.Reader); bundled automatically in the Flatpak
gse-profiler/
├── app/ # GTK4 Python application
│ ├── main.py
│ ├── ui/
│ │ ├── extension_manager.py
│ │ ├── extension_list.py
│ │ ├── details_view.py
│ │ ├── log_viewer.py
│ │ ├── profiler_view.py
│ │ ├── profiler/ # flamegraph, swimlane, histogram widgets
│ │ └── inspector_view.py
│ └── core/
│ ├── dbus_client.py # D-Bus proxy for gnome-shell APIs
│ ├── socket_server.py # Unix socket server (async)
│ ├── bridge_manager.py # bridge install / update / hash check
│ └── journal_reader.py # systemd journal reader (systemd.journal.Reader)
├── bridge-extension/ # GJS GNOME Shell extension
│ ├── extension.js
│ ├── profiler.js
│ ├── inspector.js
│ ├── socket_client.js
│ └── metadata.json
├── build-aux/ # Flatpak manifest and launcher
├── data/ # .desktop, AppStream metainfo, icons
├── docs/ # architecture diagrams
├── scripts/ # setup / uninstall helpers
└── tests/ # pytest unit tests
See CONTRIBUTING.md for the full guide — development setup, local checks, scripts, and CI/release automation.
If you find GSE Profiler useful or it saved you some time during debugging, consider supporting it on Ko-fi.
GPL-3.0-or-later — see LICENSE.