Kepler.Source.VM (Kepler v0.1.0)

Copy Markdown View Source

Tier 2: whole-node statistics, read on the tick.

Three groups, each sampled at most once per tick no matter how many watches read from it.

vm: :memory

Everything :erlang.memory/0 returns: :total, :processes, :processes_used, :system, :atom, :atom_used, :binary, :code, :ets. All in bytes.

vm: :system

:process_count, :port_count, :atom_count, :run_queue, :reductions.

:reductions has a side effect

Reading it calls :erlang.statistics(:reductions), which resets the VM's "reductions since last call" counter for the whole node. If something else in your system reads that value, declare this watch knowing it will see zeros. Kepler's own budget accounting deliberately avoids it and uses per-process reductions instead.

vm: :scheduler

:utilization, as a fraction between 0.0 and 1.0. Declaring it turns on the VM's scheduler_wall_time flag, which has a small but real steady-state cost — that is why it is opt-in rather than always reported. The first tick after startup reports no data, since a utilization needs two samples.

Summary

Types

Carried between ticks so cumulative counters can be differenced.

Functions

An empty sampling state.

Samples every VM watch.

Types

state()

@type state() :: %{scheduler: [tuple()] | nil}

Carried between ticks so cumulative counters can be differenced.

Functions

initial_state()

@spec initial_state() :: state()

An empty sampling state.

sample(watches, state)

@spec sample([Kepler.Watch.t()], state()) ::
  {%{required(atom()) => number() | :no_data}, state()}

Samples every VM watch.

Each group is read once and shared, so ten memory watches cost one :erlang.memory/0.