Skip to content

todevelopers/gseprofiler

Repository files navigation

GSE Profiler

GSE Profiler

CI Release License: GPL-3.0 ko-fi

A developer toolkit for people who write GNOME Shell extensions. It's a native GTK4 / libadwaita app, so it looks and behaves like the rest of your desktop instead of some dev tool bolted on the side.

The way it works: it drops a small bridge extension into the running shell and instruments your extension at runtime, without you touching a single line of your own code. From there you can profile function timing and look at it as a flamegraph (the call tree and timing), a swimlane (when and how often each function runs), or a histogram (where the time actually goes). Logs get filtered down to one extension UUID so you're not digging through the whole journal, and the inspector lets you open up any running extension object and walk its properties, values and nested objects while it runs.


Features

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. Captures the gnome-shell process by default (that's where all extensions actually run), with structured capture controls (scope, boot, source preset, priority) and a raw journalctl override when you want full control. Filter by log level, extension tag, and full-text search 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

How It Works

gse-profiler architecture diagram

There are two parts: a GTK4 app (Python) and a bridge GJS extension (gse-profiler-bridge@todevelopers) that the app installs for you on first launch. The bridge runs inside the gnome-shell process itself, which is what gives it direct, in-process access to every loaded extension and its live objects and functions.

You have to restart GNOME Shell once after the bridge lands. The app just asks you to log out and back in (Wayland only; X11 sessions aren't supported).

The main window has a live connection indicator, so you always know whether the bridge is actually talking to the app.

Communication

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) goes through the regular org.gnome.Shell.Extensions D-Bus interface. The socket is only there for the stuff D-Bus is bad at: high-frequency profiling events and live inspection results. Neither path needs elevated permissions.

Monkey-patching and overhead

When you start profiling, the bridge walks the extension's stateObj (its full prototype chain plus one level of owned child objects, like _indicator) and wraps every enumerable function it finds. You don't change anything in your extension's source, and all the patches are fully reversed the moment 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
  • Async timing: an async function is only recorded until it returns its Promise, usually at the first await. The time spent waiting, and the continuations that run after each await, don't count toward the event's duration. So async methods show their synchronous setup cost, not their real end-to-end latency.

Limits

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

Profiler views

All three views run on the same event data, they just answer different questions:

Flamegraph. The call tree laid out on a real-time axis. Each bar is one call, width is duration, nesting shows caller/callee depth. Best when you want to see what called what and catch call stacks that are unexpectedly deep or wide.

Swimlane. One horizontal lane per function, with idle gaps squeezed out. Every invocation is its own segment on that function's row, so you can see when and how often a function runs without the call-depth nesting getting in the way.

Histogram. Functions ranked by self-time, the wall-clock time spent in a function's own code, not counting its callees. This is the fastest way to answer where is the time actually going?, because it ignores time that really belongs to the callee.


Gallery

gse-profiler-preview

πŸ“Έ Screenshots (click to expand)
Flamegraph profiler Swimlane profiler Histogram profiler Inspector Live log viewer

Install

Requires GNOME Shell 46+ in an active Wayland GNOME session.

Option 1: Flatpak remote (recommended)

Add the self-hosted stable remote once, then install. You get automatic updates with flatpak update from then on:

flatpak remote-add --user --if-not-exists todevelopers \
  https://todevelopers.github.io/flatpaks/todevelopers.flatpakrepo
flatpak install --user todevelopers io.github.todevelopers.GseProfiler
flatpak run io.github.todevelopers.GseProfiler

The remote is GPG-signed and the key is embedded in the .flatpakrepo file, so no --no-gpg-verify is needed.

Want early test builds (rc/beta)? Add the testing remote instead: https://todevelopers.github.io/flatpaks/todevelopers-testing.flatpakrepo

Option 2: Flatpak bundle

Prefer a one-off install without adding a remote? Grab the .flatpak bundle from the latest release and install it (you won't get automatic updates this way):

flatpak install --user gseprofiler-*.flatpak
flatpak run io.github.todevelopers.GseProfiler

Option 3: One-line source install

curl -fsSL https://raw.githubusercontent.com/todevelopers/gseprofiler/main/scripts/setup-and-run.sh | bash

The script checks for GTK4 / libadwaita, clones the repo to ~/gse-profiler and launches the app. No sudo, no prompts. Run the same command again later and it pulls the latest changes and restarts the app.

Uninstall

curl -fsSL https://raw.githubusercontent.com/todevelopers/gseprofiler/main/scripts/uninstall.sh | bash

Removes the app, desktop entry, icon, and bridge extension. Nothing else on your system is touched.


Requirements

  • GNOME Shell 46+ (tested up to 50)
  • Python 3.11+
  • GTK 4 and libadwaita 1
  • PyGObject (GTK4 bindings)
  • python3-systemd, needed for the log viewer (systemd.journal.Reader); bundled automatically in the Flatpak

Project Structure

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

Contributing

See CONTRIBUTING.md for the full guide: development setup, local checks, scripts and CI/release automation.


Support

If you find GSE Profiler useful or it saved you some time during debugging, consider supporting it on Ko-fi.

ko-fi


License

GPL-3.0-or-later. See LICENSE.

About

A developer toolkit for GNOME Shell extension authors. GSE Profiler installs a lightweight shell extension bridge inside the running shell process and gives you live function timing (flamegraph/swimlane/histogram), structured log filtering, and object inspection. All from a native GTK4 interface, with zero changes to your extension's code.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Sponsor this project

Contributors