Documentation
¶
Overview ¶
Package mem reads the memory of the machine magus is running on, and of the process trees it starts there.
It sits under internal/sys because that is what it is: the layer that asks the operating system about itself, in the sense golang.org/x/sys and syscall use the word. Not to be confused with internal/memory, which is magus's own durable memory store, or with the host modules in std/, which are "host" in the language-embedding sense.
TotalBytes is a property of the machine class and is what the CI shard planner budgets against. UsableBytes narrows that by any ceiling this process actually runs under, such as a container's. AvailableBytes and SwapUsedBytes are properties of this moment and are what the run-time watchdog watches. TreeBytes totals a live process tree, which is the one figure the kernel will not fold for us. All return 0 for UNKNOWN; callers must branch on that rather than read it as "no memory".
Index ¶
- Constants
- func AvailableBytes(_ context.Context) int64
- func BudgetMB(usableBytes int64) int
- func LimitBytes(_ context.Context) int64
- func SwapUsedBytes(_ context.Context) int64
- func TotalBytes(_ context.Context) int64
- func TreeBytes(pid int) int64
- func UsableBytes(ctx context.Context) int64
- func Watch(ctx context.Context, report func(Reading))
- type Reading
Constants ¶
const ( // LowHeadroomDivisor sets the threshold: the watchdog talks below total/8. Above // it a build is using the machine, which is what a build is for. // // This is the OBSERVED half and it only ever warns. What magus queues or refuses on // is the DECLARED half, cache.MachineBudget, so the same command reaches the same // verdict whatever else the machine happens to be doing. LowHeadroomDivisor = 8 )
const UsableFraction = 0.75
UsableFraction is the share of a machine's memory that build work may plan against. The remainder is not slack: it is the OS, the editor, the browser, the agent processes driving the build, and every toolchain cache that is memory-mapped rather than resident. A planner that budgets the whole machine is budgeting memory that was never available.
One constant with two readers on purpose: the CI shard planner sizing a shard and the run-time admission gate sizing a host. They ask the same question, and two figures that drifted apart would be two different answers to it.
Variables ¶
This section is empty.
Functions ¶
func AvailableBytes ¶
AvailableBytes reads MemAvailable from /proc/meminfo, or 0 when it cannot.
MemAvailable, not MemFree: the kernel's estimate of what a new allocation could get without swapping, which counts reclaimable page cache. MemFree on a runner mid-build sits near zero on every healthy run, so a watchdog wired to it would fire constantly.
ctx is unused here; see TotalBytes.
func BudgetMB ¶
BudgetMB is the memory build work may plan against, in megabytes: usable narrowed by UsableFraction. Zero when the host is unmeasurable, which every caller reads as no budget to arbitrate rather than as a budget of nothing.
func LimitBytes ¶
LimitBytes reports the memory ceiling this process actually runs under, or 0 when there is none to read.
Both files are world-readable, so this needs no elevated privileges; a kernel without cgroups, a host outside a container, and a sandbox that hides the mount all fall through to 0 rather than erroring.
func SwapUsedBytes ¶
SwapUsedBytes reports SwapTotal minus SwapFree from /proc/meminfo, or 0 when either line is missing or unparsable.
A machine with swap disabled reports a total of 0 and so a used of 0, which is the right answer: there is no swap for pressure to show up in, and the watchdog has only AvailableBytes to go on.
ctx is unused here; see TotalBytes.
func TotalBytes ¶
TotalBytes reads MemTotal from /proc/meminfo, or 0 when it cannot.
MemTotal, not MemAvailable: the shard planner budgets against the machine class the shards will run on, not against whatever happened to be free on the machine doing the planning.
ctx is unused here (reading /proc cannot block meaningfully) and present so the signature matches darwin's, which forks a subprocess.
func TreeBytes ¶
TreeBytes sums the resident memory of pid and every descendant, or 0 when the tree cannot be read.
/proc/<pid>/task/<tid>/children rather than a scan of the whole table: the kernel already maintains the child list, so the walk touches only the tree it is asked about. A pid that exits mid-walk simply stops contributing.
func UsableBytes ¶
UsableBytes is the memory THIS PROCESS may actually commit: the machine's total, narrowed by any ceiling the process runs under.
The distinction TotalBytes cannot make. In a memory-limited container the machine reports its own RAM, which the process can never have, so a budget computed from it admits work the OOM killer then takes: magus would report a scheduling success and a killed build. Every caller sizing work THIS process will run wants this; only a caller planning for a DIFFERENT machine wants TotalBytes.
A limit at or above the machine's total is treated as no limit, which is what makes reading the raw cgroup files safe: both cgroup versions spell unlimited as a sentinel near the top of the address space, and neither sentinel has to be recognized to be discarded here.
func Watch ¶
Watch reports memory trouble until ctx is done, calling report when headroom first falls below an eighth of total, on every further 250MB drop, and whenever this run has pushed another 512MB into swap. It reads the host itself, and returns at once when the host cannot be measured.
Silent on a healthy run. A killed runner never lets magus reach its summary, so only what it already streamed survives; a watchdog that chattered would be scrolled past.
report takes the whole reading rather than a sentence, so the caller owns the wording and whatever else the warning carries.
Types ¶
type Reading ¶
type Reading struct {
AvailableBytes int64
TotalBytes int64
SwapUsedBytes int64
// SwapGrowthBytes is the rise since the run's first sample, which is what
// attributes swap to THIS run.
SwapGrowthBytes int64
// SwapTriggered reports that swap growth, not falling headroom, is why this was
// reported. Word the warning from this rather than from SwapGrowthBytes being
// non-zero: a headroom warning routinely carries a few megabytes of drift.
SwapTriggered bool
}
Reading is one sample of the host's memory, as the watchdog saw it.
SwapUsedBytes is 0 both on a platform that cannot report it and on a machine with swap disabled; treat it as UNKNOWN, not as "nothing is swapped".