Documentation
¶
Overview ¶
Package machine reads the numbers that decide whether the workstation survives the next hour: RAM and swap, memory pressure, the Claude Desktop cgroup, tmpfs and disk, build slots, kind clusters and the kernel's OOM kills. Linux only: /proc, cgroup v2 and the journal.
Index ¶
- Constants
- func FindMemcapScope(unit string) string
- func FindScope() string
- func GTTUsedMiB(gpus []GPU) int
- func ParseOllamaLoads(journal string) map[string]string
- func ParseOomd(journal string) []string
- func ReadCPUPSISome10() (float64, error)
- func ReadForks() (uint64, error)
- func ReadLoad() ([3]float64, error)
- func ReadPSIFull60() (float64, error)
- func UnloadLemonade(ctx context.Context, url, model string) error
- func UnloadOllama(ctx context.Context, url, model string) error
- func Zone() (*time.Location, error)
- type BuildCPU
- type Cluster
- type Disk
- type GPU
- type HostModel
- type Mem
- type OOMDSwap
- type OOMKill
- type Scope
- type Slot
- type SlotSlice
Constants ¶
const ( Ollama = "ollama" Lemonade = "lemonade" )
The host's model servers, which load models into the iGPU's memory.
Variables ¶
This section is empty.
Functions ¶
func FindMemcapScope ¶ added in v0.14.0
FindMemcapScope returns the cgroup of the capped run's scope unit (as a run event names it, "memcap-….scope") in its slot's slice or in memcap.slice itself, or "" when it has ended.
func FindScope ¶
func FindScope() string
FindScope returns the largest Claude Desktop scope, or "" when none runs.
func GTTUsedMiB ¶ added in v0.38.0
GTTUsedMiB is the GTT all devices pin.
func ParseOllamaLoads ¶ added in v0.38.0
ParseOllamaLoads maps each model blob to the address of the client whose request loaded it last: the first model request logged after the load that started before it (gin logs a request when it ends, with its latency). A blob whose last load's request is still running maps to "": an earlier load's client is not its.
func ParseOomd ¶ added in v0.45.2
ParseOomd keeps the lines of systemd-oomd's journal where it killed a cgroup: the event that takes every session down at once.
func ReadCPUPSISome10 ¶ added in v0.38.1
ReadCPUPSISome10 returns CPU pressure "some avg10" in percent: the share of the last ten seconds in which at least one runnable task waited for a CPU.
func ReadForks ¶ added in v0.42.0
ReadForks returns the processes forked since boot: the "processes" line of /proc/stat.
func ReadPSIFull60 ¶
ReadPSIFull60 returns memory pressure "full avg60" in percent: the share of the last minute in which every task stalled on memory.
func UnloadLemonade ¶ added in v0.45.0
UnloadLemonade asks the Lemonade Server at url to drop model from memory now (POST /api/v1/unload); its weights stay on disk.
func UnloadOllama ¶ added in v0.40.0
UnloadOllama asks the ollama at url to drop model from memory now; its weights stay on disk and the next request loads it again.
Types ¶
type BuildCPU ¶ added in v0.107.0
type BuildCPU struct {
// Quota is the slice's cpu.max as a systemd CPUQuota ("1200%" is
// twelve cores), "max" for none.
Quota string `json:"quota"`
// Weight is the slice's cpu.weight, against the desktop's slices (100
// each).
Weight int64 `json:"weight"`
// UsePct is the CPU the slice used over the sample in percent of one
// core, comparable to Quota.
UsePct float64 `json:"usePct"`
// Throttled is how many quota periods of the sample the slice ran
// into its quota in.
Throttled int64 `json:"throttled"`
}
BuildCPU is memcap.slice's CPU: the budget every capped run shares and its use over a sample.
func ReadBuildCPU ¶ added in v0.107.0
ReadBuildCPU reads memcap.slice's CPU over a sample of the given length, or nil while the slice has no cgroup (no run since the user manager started).
type Cluster ¶
type Cluster struct {
Name string `json:"name"`
Nodes int `json:"nodes"`
MemMiB int `json:"memMiB"`
// Containers are the node containers' full ids.
Containers []string `json:"containers"`
// RunningFor is docker's age of its oldest node ("2 hours ago").
RunningFor string `json:"runningFor"`
}
Cluster is a running kind cluster.
type Disk ¶
type Disk struct {
Path string `json:"path"`
UsedMiB int `json:"usedMiB"`
FreeMiB int `json:"freeMiB"`
// TotalMiB is the filesystem's size.
TotalMiB int `json:"totalMiB"`
}
Disk is a filesystem's usage in MiB.
type GPU ¶ added in v0.38.0
type GPU struct {
Device string `json:"device"`
GTTUsedMiB int `json:"gttUsedMiB"`
GTTTotalMiB int `json:"gttTotalMiB"`
VRAMUsedMiB int `json:"vramUsedMiB"`
VRAMTotMiB int `json:"vramTotalMiB"`
}
GPU is an amdgpu device's memory in MiB. On an APU, GTT is system RAM the driver pins for the GPU: no cgroup counts it, but it is gone from MemAvailable all the same.
type HostModel ¶ added in v0.45.0
type HostModel struct {
// Server is the model server holding it: Ollama or Lemonade.
Server string `json:"server"`
Name string `json:"name"`
SizeMiB int `json:"sizeMiB"`
// GPUMiB is the part on the GPU: on an APU, GTT.
GPUMiB int `json:"gpuMiB,omitempty"`
ExpiresAt time.Time `json:"expiresAt,omitzero"`
// Client is who sent the request that loaded it, as the server's log
// shows it or, while that request still runs, as the one client
// connected to the server: an address, with the kind node's name when
// one has it.
Client string `json:"client,omitempty"`
}
HostModel is a model one of the host's model servers holds in memory.
func LemonadeModels ¶ added in v0.45.0
LemonadeModels asks the Lemonade Server at url which models it holds (GET /api/v1/health) and how large each one's weights are (GET /api/v1/models, in GB). Lemonade logs no client address: a model's client is the one peer connected to url's port, and none when several are. An empty url means none is watched.
func OllamaModels ¶ added in v0.38.0
func OllamaModels(ctx context.Context, url, unit string, serviceLog func(ctx context.Context, unit, grep string) ([]byte, error)) ([]HostModel, error)
OllamaModels asks the ollama at url which models it holds (GET /api/ps) and finds each one's loading client in the log of unit (serviceLog). A load whose request still runs is not in the journal yet: its client is the one peer connected to url's port, and none when several are. An empty url means none is watched.
func (HostModel) Label ¶ added in v0.45.0
Label is the model's name after its server's, as the watch prints it.
type Mem ¶
type Mem struct {
TotalMiB int `json:"totalMiB"`
AvailableMiB int `json:"availableMiB"`
SwapTotalMiB int `json:"swapTotalMiB"`
// SwapUsedMiB is SwapTotal − SwapFree: the swap slots in use, on disk
// and in zswap alike.
SwapUsedMiB int `json:"swapUsedMiB"`
// ZswappedMiB is the swapped-out memory zswap holds compressed in RAM
// (Zswapped), ZswapPoolMiB the RAM its pool takes for it (Zswap).
ZswappedMiB int `json:"zswappedMiB,omitempty"`
ZswapPoolMiB int `json:"zswapPoolMiB,omitempty"`
// ZswapWrittenBackMiB is zswap's writeback since boot: the pages zswap
// moved from its pool to the swap device (zswpwb of /proc/vmstat), which
// grow disk swap without new swapping; 0 on a kernel without the counter.
ZswapWrittenBackMiB int `json:"zswapWrittenBackMiB,omitempty"`
ShmemMiB int `json:"shmemMiB"`
}
Mem is /proc/meminfo in MiB, with zswap's writeback from /proc/vmstat.
func ReadMem ¶
ReadMem parses /proc/meminfo and zswap's writeback from /proc/vmstat; an unreadable /proc/vmstat leaves the writeback 0.
func (Mem) DiskSwapMiB ¶ added in v0.73.0
DiskSwapMiB is the swap in use that zswap does not hold: the pages written to the swap device, the share that costs a disk read to bring back.
type OOMDSwap ¶ added in v0.73.0
type OOMDSwap struct {
// LimitPercent is SwapUsedLimit in whole percent of SwapTotal.
LimitPercent int `json:"limitPercent"`
// Monitored are the swap-monitored cgroups' paths.
Monitored []string `json:"monitored,omitempty"`
}
OOMDSwap is what systemd-oomd does about swap: its SwapUsedLimit and the cgroups it watches for it (ManagedOOMSwap=kill). Past the limit it kills the largest swap user among them, however much RAM is free; with none watched it never kills for swap.
func ParseOomctlDump ¶ added in v0.73.0
ParseOomctlDump reads the "Swap Used Limit" and the paths under "Swap Monitored CGroups" of an `oomctl dump`.
func ReadOOMDSwap ¶ added in v0.73.0
ReadOOMDSwap asks systemd-oomd for its swap rule (`oomctl dump`); an error when oomd does not answer.
func (OOMDSwap) HeadroomMiB ¶ added in v0.73.0
HeadroomMiB is the swap growth left before the trigger; negative once swap is past it. oomd counts every swap slot in use, zswap's included.
type OOMKill ¶
type OOMKill struct {
At time.Time `json:"at"`
PID int `json:"pid"`
Task string `json:"task"`
AnonMiB int `json:"anonMiB"`
Constraint string `json:"constraint,omitempty"`
// Memcg is the cgroup whose limit was hit: a memcap scope or slot
// slice, a kind lab's pod, or the desktop scope.
Memcg string `json:"memcg,omitempty"`
// TaskMemcg is the killed task's cgroup: its run's scope when the cap
// of the slot slice the run shares was hit.
TaskMemcg string `json:"taskMemcg,omitempty"`
}
OOMKill is one process the kernel killed for memory.
type Scope ¶
type Scope struct {
Path string `json:"path"`
CurrentMiB int `json:"currentMiB"`
AnonMiB int `json:"anonMiB"`
SwapMiB int `json:"swapMiB"`
High string `json:"high"`
Max string `json:"max"`
SwapMax string `json:"swapMax"`
OOMKills int64 `json:"oomKills"`
HighEvents int64 `json:"highEvents"`
}
Scope is the Claude Desktop cgroup: every session's CLI and MCP servers live in it, and systemd-oomd kills it whole.
type Slot ¶
type Slot struct {
N int `json:"n"`
Free bool `json:"free"`
Holder string `json:"holder,omitempty"`
}
Slot is one of memcap's build slots.
type SlotSlice ¶ added in v0.145.4
type SlotSlice struct {
// Scopes are the run scopes in the slice that still hold processes.
Scopes []string
// CurrentMiB is the slice's memory, every scope's together.
CurrentMiB int
}
SlotSlice is a build slot's slice: the runs still in it and its memory.
func ReadSlotSlice ¶ added in v0.145.4
ReadSlotSlice reads the slot slice (as a Cap names it, "memcap-slot1_….slice"), nil when it has no cgroup.