Skip to content

perf(sessioncmd): read every listed session's directory from one tmux call - #301

Open
tfyl wants to merge 3 commits into
mainfrom
perf/board-list-one-tmux-call
Open

tfyl wants to merge 3 commits into
mainfrom
perf/board-list-one-tmux-call

Conversation

@tfyl

@tfyl tfyl commented Oct 9, 2026

Copy link
Copy Markdown
Member

Why

A board with extensions installed spends most of a CPU core forking tmux. In a 60 s execsnoop of a live board with 149 sessions, the board process forked about 4,450 processes a minute. 2,073 of them were tmux list-panes -t gi_<id> -F #{pane_current_path}, and the board's tmux server sat at about 36% of a core answering them.

Summary

  • An extension that lists the whole board on every poll pass (the 2 s poll_interval) goes through BoardList. For each running row, sessionInfo forked one list-panes to read the pane's directory. That came on top of the list-panes -a the listing had already run to see which rows are alive. With about 70 running sessions, that is 70 forks per pass, or 30 passes × 70 ≈ 2,100 forks a minute, which is the 2,073 measured.
  • The liveness scan now reads #{pane_current_path} next to the pid (PaneScan.Paths), and the listing takes each running row's directory from it. Adopted panes, and a pane with no directory, still go through PaneCurrentPath as before.
  • A directory name can contain a line break, and the scan would read the text after it as a pane line of its own. A pane's occupant could use that to forge liveness or a pid for any session. Each scan therefore starts every line with a random per-call mark. An unmarked line is treated as the rest of the directory above it, and that cut-short directory is dropped, so the old per-session read answers for it as before.

Numbers

TestBoardListReadsEveryDirectoryInOneTmuxCall counts the tmux processes one BoardList forks, through a logging shim, with 7 running sessions:

tmux forks per listing
before 8 (1 + N)
after 1

On the board measured above, that removes the 2,073/min list-panes -t … pane_current_path line, about 47% of every fork the board made. Live execsnoop figures after deploy will be added in a comment.

Test plan

  • TestBoardListReadsEveryDirectoryInOneTmuxCall: fails on main with 8 forks, passes here with 1; also checks that a pane which cd'd reports its new directory
  • TestPaneScanIgnoresALineBreakInADirectory: a forged line after a line break neither adds a session nor overrides a pid, and a trailing space in a path is kept
  • go test -count=1 ./internal/tmux/ ./internal/sessioncmd/ ./internal/extensionhost/ pass, run with env -u TMUX -u TMUX_PANE and a scratch TMUX_TMPDIR
  • go test -short ./internal/ui/: passes except TestFocusedAdoptedPaneEchoesQuicklyWithNoClientOfItsOwn (257 ms against a 250 ms wall-clock budget, with two other suites running in parallel), which passes 3/3 run on its own
  • Nothing the board draws changes, so this PR has no capture

🤖 Generated with Claude Code

tfyl and others added 2 commits October 9, 2026 17:47
… call

A board listing forked `tmux list-panes -t gi_<id> -F #{pane_current_path}`
for every running row, on top of the `list-panes -a` that proved the rows
alive. The charters extension lists the whole board on every poll pass, so
that was one fork per running session every two seconds. The liveness scan
now reads pane_current_path alongside the pid and the listing uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s scan

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tfyl tfyl added the claude PR primarily authored by Claude label Oct 9, 2026
The board's poll pass scans every pane every two seconds and never shows a
directory, but the scan had started reading each pane's current path and
prefixing every line with a fresh mark for it. At 70 panes that cost 39%
more bytes and 8% more allocations per scan. ScanPanes is back to the
liveness-only listing; ScanPanesWithPaths carries the directories, and
only the session listing uses it, so it keeps its one tmux call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude PR primarily authored by Claude

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant