Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Starf An activity monitor for Apple silicon that never invents a number e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Licence & updates
Licensing
Updates
Elyra
Measurement

Measurement

What a number in Starf means, and when there is deliberately no number.

Unavailable is not zero

macOS shares a process's detailed counters only when it chooses to. On an ordinary desktop about a quarter of processes deny them — on the machine this was built on, 341 of 1,245. Two calls can be refused independently, so a process carries two flags:

Flag Set when Guards
accessible proc_pidinfo(PROC_PIDTASKINFO) answered CPU, CPU time, memory, threads
ioAccessible proc_pid_rusage(RUSAGE_INFO_V4) answered bytes read and written, wake-ups, physical footprint

A counter behind a flag that is false was never measured. It renders as , exports as an empty CSV cell and a JSON null, and sorts below every real value in both directions — because an unavailable counter is not a small one.

A measured zero is a measurement. It renders as 0, exports as 0, and sorts with the other numbers.

Refused is not gone

Two different things stop Starf reading a process, and the kernel distinguishes them:

Kernel answer Meaning Reported as
EPERM the process exists; macOS will not describe it "macOS does not share this process's details."
ESRCH no such process "This process has exited."

Conflating them would be the easiest mistake in the codebase to make, and it was made once: the first per-thread probe against WindowServer reported it as exited when it was running perfectly well. sys::process::identity now returns Same, Gone or Denied, and every message downstream says which.

Identity

A PID on its own is not an identity — macOS reuses them. Everything that has to survive across samples is keyed by PID and start time together:

  • rate baselines, so a new process never inherits the previous holder's counters
  • GPU observed time
  • tree expansion state, so a recycled PID does not arrive collapsed
  • per-thread baselines

Before Starf sends a signal to a process, or reads a diagnostics page, it re-checks that the PID still names the same process, with the same start time and owner. Diagnostics checks again after the read, so a process that exits mid-walk cannot have its successor's files attributed to it.

Rates

Every rate is a difference between two readings divided by the time actually measured between them, never by the interval that was asked for.

Three rules apply everywhere:

  • A first reading has no rate. It establishes a baseline and reports nothing. A process, processor, thread or GPU client seen for the first time shows , not 0.
  • A counter that went backwards is a reset. It reports nothing rather than a negative rate or a wrapped-around spike.
  • A gap too long to describe one interval is not differenced. Per-thread collection refuses a gap over thirty seconds, which is what a paused monitor or a sleeping machine produces.

Host CPU tick counters are 32 bits and wrap. The difference is taken with wrapping subtraction, which is the real elapsed count as long as fewer than 2³² ticks passed.

Scales

Two CPU scales exist, and mixing them up would make every number wrong by the processor count:

Where Scale On a 16-processor machine
Process list, per-thread charts 100% is one logical processor a process on four processors reads 400%
CPU card, breakdown, per-processor tiles 100% is the whole machine user + system + idle is always 100

A per-processor tile is 0–100% of that one processor, whatever the machine's count.

GPU percentages are driver-reported GPU time over elapsed time. Overlapping work can exceed 100%.

Totals that do not add up, and why that is correct

Host CPU against the process column. The host counters include kernel work and the processes that deny their own counters. The visible process column does not sum to the host total, and forcing it to would mean inventing the difference.

Subtree totals against each other. A parent's Σ includes its children's, so adding the visible rows double-counts. The overview charts are what measure the machine.

Memory against the machine. Process memory counters are summed as reported. Shared mappings overlap between processes, so a subtree's memory is not unique physical RAM and not what quitting it would free.

Resident against virtual. Resident pages are part of a virtual mapping. The two are selectable measures, never stacked.

Bounds and coverage

A subtree total tracks how many of its members reported the counter:

Shown Meaning
a number every member reported; this is the total
and a number some member did not report, or the sum reached an arithmetic limit; this is a lower bound
no member reported

Above 2⁵³ a f64 no longer represents every integer, so a sum past that point is flagged as a bound rather than passed off as exact. No real counter comes close; the guard exists so a corrupt one cannot slip through.

Retention

Histories keep fifteen minutes. Per-processor and per-thread series keep at most 901 points each under a shared budget of 131,072 points, so a machine with very many processors keeps a shorter history rather than an unbounded one. An unavailable reading is stored as a gap, and the chart breaks its line there instead of drawing through time it did not measure.