Skip to content

feat: collect the running instance's goroutine stacks in lich rage - #246

Merged
omartelo merged 1 commit into
mainfrom
fix/goroutine-dump-in-rage
Aug 12, 2026
Merged

omartelo merged 1 commit into
mainfrom
fix/goroutine-dump-in-rage

Conversation

@omartelo

Copy link
Copy Markdown
Collaborator

Why

Two Windows users report the same failure: lich freezes, the window stays on screen, the UI stops talking to the backend — and lich.log holds nothing.

Nothing is missing on the crash side. debug.SetCrashOutput has routed panics to lich.log since v0.23.0 (#124, which was this exact shape on Windows), and there is no recover() anywhere, so any panic kills the process and leaves its trace in the log. An empty log therefore says the opposite of what it looks like: it probably did not panic. It hung, or it was killed without one.

For that case lich collects nothing at all. Every diagnostic it has answers the question "is it running?" — none answers "what is it stuck on?".

What

  • internal/terminal/transport.go — a token-gated GET /debug/goroutines serving pprof.Lookup("goroutine").WriteTo(w, 2). Deliberately not net/http/pprof: one dump of one thing, not a profiling surface registered on the default mux.
  • internal/rage/rage.go — lich rage fetches it into the bundle as goroutines.txt whenever an instance is answering, masked by the same scrub the logs go through. The fetch is bounded at 5s, and a lich that holds its port but will not answer within it leaves that sentence in the file — which is the finding, not a failure of the bundle.
  • No dump entry at all when nothing is running: the report already says so.

Nothing else changes, and the endpoint costs nothing until someone asks for it.

Test plan

  • go test ./... — 1304 pass, including the new endpoint (200 with the stacks, 401 without the token) and the three rage cases (running, running-but-silent, not running).
  • go test -race ./internal/rage/... ./internal/terminal/...
  • gofmt -l . clean, go vet ./... clean.
  • Cross-compile loop: build + vet for linux, darwin, windows.
  • Manual: freeze a session, run lich rage, read goroutines.txt out of the archive.

Frontend untouched, so its gate was not run.

A lich whose window stops updating while the process is still alive writes
nothing to lich.log: nothing panicked, nothing exited, and a panic already
reaches the log (debug.SetCrashOutput, v0.23.0). So a freeze report arrives
with no evidence in it at all.

The transport gains a token-gated /debug/goroutines serving
pprof.Lookup("goroutine") at debug level 2 — one dump of one thing, not
net/http/pprof's whole surface — and `lich rage` fetches it into the bundle
as goroutines.txt whenever an instance is answering. A five-second bound on
the fetch keeps the bundle finite; an instance that holds its port and will
not answer within it leaves that sentence in the file, which is itself the
finding.
@omartelo
omartelo merged commit d2e9aa5 into main Aug 12, 2026
4 checks passed
@omartelo
omartelo deleted the fix/goroutine-dump-in-rage branch August 12, 2026 19:07
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