Skip to content

feat(diagnostics): say what the app was doing when it froze - #82

Merged
rocketraman merged 3 commits into
masterfrom
claude/gitvantage-diagnostics
Nov 3, 2026
Merged

rocketraman merged 3 commits into
masterfrom
claude/gitvantage-diagnostics

Conversation

@rocketraman

Copy link
Copy Markdown
Owner

Why

The 1.5.0 lag report (see #81) left several numbers that had to be guessed:

  • UI freezes: 18 of 20 stall reports showed only NativeTaoBridge.nativeRunBlocking. The UI thread was stuck in native code (AppKit or the allocator), waiting on something another thread held, and the report said nothing about the other threads.
  • Allocation: the GC rate (~2.5 young collections/s, ~6% of time) implied ~1 GB/s allocated, but only by assuming the native image's young-generation default. A fixture at the same scan rate allocates 1–2 MB/s, so the source is still unknown.
  • Memory: the 1.7 GB footprint couldn't be compared with the ~55 MB heap over time.
  • Rescans: ~34 a minute, with no way to tell which repos or which folders caused them.

What changed

  1. Allocation rate, in each minutely perf line, split by thread group (AllocationMeter). For example alloc 2.6MB/s over 60s (ui 50% · workers 46% · other 3%). The clipboard report gets the total since launch. It uses the per-thread allocation counters and says "unavailable" if the runtime lacks them.
  2. Stall context, always on like the stall report:
    • A context line: git processes started during the stall and running when caught, scans in progress, and collections and GC time during the stall.
    • Other threads' stacks (ThreadSummary). Identical stacks are folded with a count. Idle threads are counted, not printed. A thread waiting in a native call counts as idle unless app code is on its stack, so a worker in forkAndExec stays visible.
  3. Resident memory in the vitals: /proc/self/status on Linux, ps on macOS (once a minute, only while collecting).
  4. Busiest watched repos (WatchStats): events, rescans, and each folder's share of the events, recorded only while collecting.

For review

  • Privacy: these reports get pasted into public issues.
    • Repos appear as repo #n (W worktrees), their position in the list, never a path or name.
    • Folders are named only when generic (build/, node_modules/, .git/index…); anything else is "(other folder)".
    • Thread names and stack frames carry no user data.
  • Architecture rules:
    • The always-on counters live in a new model/Activity, which Git and RepoScanner write and the watchdog reads. That keeps the UI layer from reaching Git.
    • Diagnostics.kt joins the process-spawning allowlist for the macOS ps call, with the reason added to the rule's comment.
  • Native image support is unconfirmed. Neither the per-thread allocation counters nor Thread.getAllStackTraces has been tried in the native image yet. Both are wrapped so a missing feature reads "unavailable" rather than failing.
  • Cost: an allocation sample reads thread names at depth 0 (no stacks) once a minute. The stall context costs one GC-bean read per 250 ms probe.

Verification

  • All 275 app tests and 10 architecture tests pass, from fresh result files. New tests cover:
    • thread grouping and the idle filter
    • folder labels and the privacy rules
    • the report format
    • allocation attribution and baseline
    • VmRSS parsing
  • Fixture run (JVM, Linux): 44 repos, with three of them churning (two in build/, one in a tracked root file). It showed:
    • The allocation line, at about 2.6 MB/s.
    • rss in the vitals.
    • The watch stats, which named exactly the three churning repos and their folders.
    • A forced stall (GITVANTAGE_DEBUG_STALL_MS), whose report included the new context line and thread summary.
  • Bugs fixed during the run:
    • The first allocation reading covered everything since startup over a 0 s window. It now takes a baseline when the reporter starts.
    • Rates under 1 MB/s rounded to 0. They now show one decimal.
    • Idle native threads (poll, the reference handler) were listed as busy.
  • Not yet observed: a stall that happens during real background work.

rocketraman and others added 3 commits November 3, 2026 12:21
The lag report could say how often git ran and that the UI thread
froze, but not why: most freezes showed only nativeRunBlocking, the
allocation rate had to be estimated from a GC flag default, and
nothing said which repos the watcher kept rescanning.

- The perf log reports the allocation rate each minute, split by UI
  thread, dispatcher workers, watcher threads and the rest; the
  clipboard report gives the total since launch.
- A stall report adds what else was going on: git processes started
  and running, scans in progress, collections during the stall, and
  the other threads' stacks, identical ones folded together and idle
  ones left out.
- The vitals include resident memory, so footprint growth can be told
  apart from heap growth.
- The perf log names the busiest watched repos, with their rescans and
  which folders their events came from. Repos appear by list position
  and only generic folder names are shown, since reports get posted
  publicly.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Raman Gupta <rocketraman@gmail.com>
The allocation baseline was taken at launch whether or not collection
was on, so switching it on mid-session gave a first reading averaged
over the whole session. It is now taken when collection starts, at
launch or from Settings, and a reading with no baseline takes one
instead of reporting a rate.

Clicking Off while GITVANTAGE_PERF=1 forces collection on now does
nothing. It used to log "collection switched off" for collection that
carried on, and save "off" for the next launch.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Raman Gupta <rocketraman@gmail.com>
A watcher overflow rescans every repo, and those repos filled the
busiest-repos list with "events=0", pushing out the one actually
churning. The list now shows only repos with events of their own; the
overflow already has its own counter. Shares too small to round now
read "<1%" rather than "0%", and "1 worktrees" is singular.

Also fixes the detekt formatting that failed CI.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Raman Gupta <rocketraman@gmail.com>
@rocketraman
rocketraman merged commit 823b45b into master Nov 3, 2026
2 checks passed
@rocketraman
rocketraman deleted the claude/gitvantage-diagnostics branch November 3, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant