82 lines
5.5 KiB
Markdown
82 lines
5.5 KiB
Markdown
# Debugging Responsiveness — sovereign_browser
|
||
|
||
## Symptoms (from user)
|
||
1. **Hover lag**: a page that highlights buttons on mouseover responds ~2× slower than in Brave.
|
||
2. **Load progress invisible until complete**: page loads "take a while" and show no progress until the download finishes.
|
||
|
||
## Confirmed root-cause candidates (from code review)
|
||
|
||
### A. Hover/scroll lag — WebKitGTK rendering pipeline
|
||
[`src/tab_manager.c:2912`](src/tab_manager.c:2912) configures `WebKitSettings` but only sets:
|
||
- `enable_developer_extras`, `enable_javascript`, `javascript_can_open_windows_automatically`
|
||
- `allow_file_access_from_file_urls`, `allow_universal_access_from_file_urls`, `allow_modal_dialogs`
|
||
|
||
**Not set** (left at WebKitGTK defaults):
|
||
- `enable_accelerated_2d_canvas` (default FALSE on many builds)
|
||
- `enable_smooth_scrolling` (default FALSE)
|
||
- compositing mode — controlled by env var `WEBKIT_DISABLE_COMPOSITING_MODE=1`, which if set forces non-accelerated compositing
|
||
|
||
WebKitGTK ships software-rendered by default on many distros; Chromium/Brave use GPU compositing + Skia by default. This alone explains ~2× paint lag on hover effects (each `:hover` triggers a repaint; software repaint is much slower).
|
||
|
||
### B. No load-progress UI
|
||
[`on_load_changed`](src/tab_manager.c:1635) handles `LOAD_STARTED` (→ stop icon), `LOAD_COMMITTED` (→ URL bar/title), `LOAD_FINISHED`/`LOAD_FAILED` (→ reload icon). There is **no** `notify::estimated-load-progress` signal handler and **no** progress bar/spinner in the tab strip. So between STARTED and FINISHED the user sees only a static stop icon — "nothing happens until done."
|
||
|
||
## Debugging plan (ordered: cheap → invasive)
|
||
|
||
### Step 1 — Confirm the rendering hypothesis with env vars (no code change)
|
||
Run the browser with GPU compositing forced on and compare hover responsiveness:
|
||
|
||
```bash
|
||
WEBKIT_DISABLE_COMPOSITING_MODE=0 ./browser.sh start --login-method generate --url <hover-test-page>
|
||
# vs
|
||
WEBKIT_DISABLE_COMPOSITING_MODE=1 ./browser.sh start --login-method generate --url <hover-test-page>
|
||
```
|
||
|
||
Also try `WEBKIT_FORCE_SANDBOX=0` only if the above is inconclusive (rules out sandbox-overhead noise). If forcing compositing on closes the gap with Brave, the fix is enabling accelerated settings + ensuring the compositor is active.
|
||
|
||
### Step 2 — Use the existing perf-probe to quantify
|
||
The browser already ships [`www/js/perf-probe.js`](www/js/perf-probe.js) (long-task observer, rAF FPS, heartbeat main-thread-blockage fallback for WebKitGTK). Enable it (Settings → perf_probe_enabled, or it's gated by `settings_get()->perf_probe_enabled` at [`tab_manager.c:1337`](src/tab_manager.c:1337)) and load the hover-test page. Compare:
|
||
- `cpu_busy_percent` (heartbeat blockage) — high = main thread is being starved
|
||
- `fps` — low fps during hover = paint-bound, points at rendering pipeline
|
||
- `active_timer_count` — high = page is timer-heavy (not our bug)
|
||
|
||
Call the MCP `processes.tabs` tool ([`agent_mcp.c:583`](src/agent_mcp.c:583)) to read the probe values per tab while hovering.
|
||
|
||
### Step 3 — System-level profiling to rule out the C/GTK main thread
|
||
Even if rendering is the cause, confirm the GTK main thread isn't also blocked. Two cheap checks:
|
||
|
||
```bash
|
||
# a) Is the UI process CPU-bound during hover?
|
||
top -p $(pgrep -f sovereign_browser) -H -d 0.5
|
||
|
||
# b) perf record the UI process for 10s while hovering, then look at hotspots
|
||
sudo perf record -p $(pgrep -f sovereign_browser) -g -- sleep 10
|
||
sudo perf report --no-children
|
||
```
|
||
|
||
Look for time in `webkit*`/`WebCore*`/`cairo`/`GLib` dispatchers. If time is dominated by `g_main_context_*` or our own handlers, the C layer is implicated; if it's `WebCore::paint`/`cairo`, it's the renderer.
|
||
|
||
### Step 4 — Check for main-thread blocking from sync JS eval
|
||
[`agent_js_eval_sync`](src/agent_snapshot.c:80) runs a nested `g_main_loop` on the default context. The agent loop ([`agent_loop.c:333`](src/agent_loop.c:333)) calls this from a background thread while pumping the main loop. If an agent/MCP tool is running during normal browsing, this can re-dispatch unrelated sources and cause UI hitches. Verify by checking whether sluggishness only occurs while an agent loop is active (watch `[agent-loop] iter` log lines). If correlated, the fix is to route sync JS eval through a dedicated `GMainContext` instead of the default one.
|
||
|
||
### Step 5 — Fix candidates (after diagnosis confirms which)
|
||
|
||
**Rendering (likely):**
|
||
- In [`src/tab_manager.c:2912`](src/tab_manager.c:2912) add:
|
||
```c
|
||
webkit_settings_set_enable_accelerated_2d_canvas(settings, TRUE);
|
||
webkit_settings_set_enable_smooth_scrolling(settings, TRUE);
|
||
```
|
||
and ensure `WEBKIT_DISABLE_COMPOSITING_MODE` is **not** set in the launch environment (check `browser.sh`).
|
||
- Verify GPU is actually being used: `WEBKIT_DEBUG=compositing` or check `glxinfo`/EGL logs.
|
||
|
||
**Load progress (confirmed gap):**
|
||
- Add a `notify::estimated-load-progress` handler on each webview that drives a thin progress bar (or the tab label background) between `LOAD_STARTED` and `LOAD_FINISHED`. WebKitGTK emits this continuously during fetch; wiring it gives the missing "loading" feedback. Add the handler next to the `load-changed` connect at [`src/tab_manager.c:3136`](src/tab_manager.c:3136).
|
||
|
||
## Recommended order
|
||
1. Step 1 (env-var A/B test) — fastest signal, no rebuild.
|
||
2. Step 2 (perf-probe) — quantifies, uses existing infra.
|
||
3. Step 3 (perf/top) — rules out C-layer blocking.
|
||
4. Step 4 — only if sluggishness correlates with agent activity.
|
||
5. Step 5 — implement the fix(es) confirmed by 1–4.
|