Files
sovereign_browser/plans/debug-responsiveness.md

82 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.