Skip to content
Anomalous Anomalous / Help
Browse the docs

What we measure, and why

Anomalous samples roughly a dozen live dimensions per process every tick — CPU, real memory, energy with a P/E-core split, disk I/O, wakeups, GPU, network, Neural Engine, temperature and power — at a fraction of a percent of CPU.

One tick, every dimension

The Anomalous Transparency panel titled What we sample and why, listing each signal in plain language.
Inside the app: every signal it samples, explained in plain language.

Roughly every 90 seconds, Anomalous takes a snapshot of every running program on your Mac. For each one it records the same fixed set of numbers: how much processor, memory, disk, energy, battery-costing wakeups, graphics chip, AI accelerator, and network that program is using. Each number is one dimension of behavior. Together they are the signals Anomalous watches so it can notice when a program that was behaving quietly starts misbehaving.

One snapshot of one program at one instant is a ProcessSample: about eleven measured dimensions plus the program's identity, a timestamp, and its own uptime. You never see these numbers scrolling by. They run silently in the background and only surface as evidence when a program crosses a threshold and gets flagged. That is why every diagnosis can say something concrete like "using far more memory than it normally does."

Anomalous never judges a raw number. Every counter is cumulative since the program started, so the detection rules measure the rate of change over a window and compare it to that program's own learned baseline. A value of 0 always means "unknown," never "reset to zero."

Under the hood

Most of a tick's data comes from a single macOS system call, proc_pid_rusage, requested at the richest flavor RUSAGE_INFO_V6. That one call returns CPU time, resident and physical memory, disk bytes, energy, wakeups, instructions, cycles, and the Neural Engine footprint together. If the kernel rejects V6 it retries at the older V4 layout into the same zeroed buffer, so V6-only fields simply read 0 = unknown instead of failing. See Collector.swift:130-160 and ProcessSample.swift:5-48.

Compute: CPU, cycles, energy, P/E split

CPU time is the processor seconds a program has burned since it started. A program pinned high for a sustained window is the classic sign of a wedged loop or runaway task. Anomalous reads both a lifetime ratio and an instantaneous rate, so a once-hot, now-idle program can heal instead of being blamed forever.

Energy is the direct battery-drain measure, reported in nanojoules across all cores. It is the honest analog of Activity Monitor's "Energy Impact." Anomalous also tracks the P/E split — how much of that energy landed on the expensive performance cores versus the efficient ones. Instructions and cycles give real work-per-cycle (IPC): the ratio tells a program doing productive busy-work apart from one just spinning idle and burning cycles.

Under the hood

CPU time is ri_user_time + ri_system_time converted with the mach timebase (Collector.swift:144). Energy is ri_energy_nj with the performance-core share in ri_penergy_nj; both are V6-only and read 0 on the V4 fallback (Collector.swift:101-105,151-152). Instructions and cycles are ri_instructions and ri_cycles (Collector.swift:110-113). The per-process uptime comes from ri_proc_start_abstime — always this process, never system uptime — and is the denominator for the CPU ratio and the warm-up gate (DetectionRules.swift:189-192).

Memory: real footprint vs RSS

Anomalous measures memory two ways and trusts the honest one. The primary number is physical footprint (phys_footprint) — the same value Activity Monitor shows in its "Memory" column, and the number the kernel actually kills a process on. Monotonic growth in footprint above a floor is a memory leak.

The older number, resident set size (RSS), is kept only as a secondary signal. A program's own memory-mapped executable can inflate RSS; in early testing RSS ran roughly 3x overstated. Building the leak rule on footprint instead of RSS means far fewer false alarms.

Why footprint, not RSS
RSS counts shared and mapped pages a program does not truly own, so it overstates real memory about threefold. phys_footprint is what the kernel budgets and enforces, so it is the number worth trusting.
Under the hood

phys_footprint reads from ri_phys_footprint and tracks a lifetime high-water mark; it falls back to RSS only when it reads 0. RSS is ri_resident_size. See ProcessSample.swift:10-20, Collector.swift:145-148, and the footprint-first leak rule at DetectionRules.swift:148-150,263-267.

I/O and wakeups

Disk I/O is the cumulative bytes a program has read and written. Sustained throughput far above its own baseline is disk thrash. The floor is set deliberately high — around 40 MB/s over ten minutes, roughly 24 GB — so only real, sustained abuse flags.

Wakeups are the quiet battery killer. A program can sit at modest CPU while waking the processor thousands of times a second by busy-polling. A 1 ms busy-poll measured about 1,400 wakeups per second, versus a healthy daemon well under 10 per second. Anomalous counts both idle and interrupt wakeups, and requires some real CPU alongside them so that an idle text editor is never falsely blamed.

Under the hood

Disk is ri_diskio_bytesread and ri_diskio_byteswritten (Collector.swift:149-150, rule at DetectionRules.swift:142-147). Wakeups are ri_pkg_idle_wkups and ri_interrupt_wkups (Collector.swift:106-109), with the CPU-corroboration guard at DetectionRules.swift:126-141.

GPU, Neural Engine, network

Three richer dimensions live outside the rusage syscall.

GPU time per process is how much of the graphics chip a program is using. Apple's GPU driver publishes one node per GPU-using process in the IOKit registry, each carrying an accumulatedGPUTime counter tagged with the owning process. Sustained use around 40% or more of the device for ten minutes is a real workload worth flagging; a compositor blip stays humanly silent.

Neural Engine (ANE) memory shows which program is holding on-device AI models in the Apple Neural Engine. It arrives free in the V6 tail of the same rusage call already issued — no extra private API.

Network bytes per process come from the same source that nettop and Activity Monitor use. Sustained throughput far above baseline — the floor is about 25 MB/s, roughly 15 GB over ten minutes — catches exfiltration or a runaway uploader, while the seasonal baseline keeps your nightly cloud backup quiet. Only byte counts are recorded; the remote endpoint is never stored.

Some processes need the helper
Unprivileged GPU and network reads only attribute programs owned by you. Root-owned daemons are covered by the separate background helper running the same samplers. See the background helper.
Under the hood

GPU time sums accumulatedGPUTime across a pid's AGXDeviceUserClient registry nodes, keyed by the pid parsed from IOUserClientCreator (GPUSampler.swift:69-129). ANE memory is ri_neural_footprint and ri_lifetime_max_neural_footprint, V6-only, 0 on V4 fallback (ProcessSample.swift:40-44, Collector.swift:114-118). Network bytes fold per-socket flow deltas from the private NetworkStatistics framework into a monotonic per-pid accumulator (NetworkStatsSampler.swift:170-188); destinations are noted in code as "a future dimension" and left unused.

Temperature and power

Alongside the per-process signals, Anomalous takes a machine-wide context snapshot each tick — memory pressure, swap, thermal state, load averages, and active core count — so it can read the same spike differently on a box under memory pressure than on an idle one.

Where the hardware sensors are readable, it also records SoC die temperature (the hottest die sensor, in °C) and per-rail power (average watts drawn by the CPU, GPU, and ANE rails). These are optional. When a sensor surface is missing on a given Mac, that dimension goes nil — honest absence, never a fake zero. On the tested unprivileged build only the GPU power rail accumulates, so the CPU and ANE rails stay unknown.

Under the hood

System context uses cheap sysctl/libc calls, each degrading independently to a 0 default (SystemSignals.swift:118-132). Die temperature is read unprivileged from the IOHID sensor grid (AppleVendor usage page 0xff00, ~38°C on test hardware); per-rail power is Δenergy/Δwall from IOReport's "Energy Model" group (Sensors.swift:37-58,144-184). These private surfaces are resolved defensively so a rename silences a dimension rather than crashing.

The cost: 0.05% CPU

All of this is nearly free. The bulk of a tick is a single proc_pid_rusage call per process, and the whole sampling tick was measured at about 0.046% CPU — under one twentieth of one percent. Nothing runs between ticks. There is no continuous polling, no fan spin-up, no measurable drain on your battery.

Nor is anything sensitive captured. Every measurement is a numeric counter — no file contents, no network payloads. The executable path is read only locally to derive a program's identity and install-source category, then discarded. Network measurement records byte counts only, never the endpoint. This is what lets Anomalous watch everything, all the time, and stay silent. For more on that boundary, see what Anomalous keeps.

The full signal list

SignalUnitWhy it reveals bad behavior
CPU timeprocessor secondsSustained high CPU is a wedged loop or runaway task
Physical footprint
primary
bytesMonotonic growth above a floor is a memory leak
RSS
secondary
bytesKept for continuity; ~3x overstated, so deprioritized
Disk I/Obytes read / writtenSustained throughput above baseline is disk thrash
EnergynanojoulesThe direct battery-drain measure
P/E core splitnanojoules on P-coresReveals work landing on the expensive high-power cores
Wakeupsidle + interrupt countBusy-polling drains battery even at low CPU
Instructions + cyclescount (IPC ratio)Productive work vs idle spinning
GPU timemach-absolute ticksSustained ~40%+ of the device is a real GPU workload
Neural Engine memorybytesWhich program is holding on-device AI models
Network bytesbytes in / outSustained throughput above baseline catches exfiltration
Per-process uptimesecondsDenominator for the CPU ratio and warm-up gate
SoC die temperature
optional
°CSilicon temperature as system context
Per-rail power
optional
wattsGround-truth power draw per subsystem
Spotted something wrong or missing? Anomalous is open source, and its process corpus takes pull requests. Contribute on GitHub →