πŸ’»πŸ”‹ macpow

May 11, 2026 Β· View on GitHub

CI Crates.io Homebrew License: MIT Platform

Real-time power consumption monitor for Apple Silicon Macs (M1–M5+).

macpow screenshot

macpow reads directly from macOS hardware interfaces β€” IOReport, SMC, IORegistry, CoreAudio, and Mach/kernel APIs β€” to show per-component power draw, temperatures, frequencies, CPU utilization, and per-process energy attribution. No sudo required.

Legend

SymbolMeaning
0.123 WMeasured power (direct hardware reading)
β‰ˆ0.123 WEstimated power (model-based calculation)
≀0.123 WUpper-bound power estimate
β–ΈPinned resource (sparkline chart visible)
β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘CPU core utilization bar (filled = busy)
37Β°CFresh temperature reading
~37Β°CStale temperature (sensor read failed, showing last known value)
pending…Data source still initializing
[dead]Process has exited (energy total preserved)
Bold whiteSection headers and measured values
GreenLow power (< 1W) or info-only rows
YellowModerate power (1–5W)
OrangeHigh power (5–10W)
RedVery high power (> 10W)
GrayDimmed/inactive items

Features

  • SoC breakdown β€” CPU (E/P cores with per-core power, utilization bars, temperatures), GPU, ANE, DRAM, GPU SRAM, Media Engine, Camera (ISP), Fabric β€” all from IOReport Energy Model
  • CPU utilization β€” per-core usage % with visual bars from Mach host_processor_info
  • Real frequencies β€” CPU and GPU MHz from DVFS voltage-states tables, not percentages
  • Temperatures β€” per-component and per-core from SMC sensors (CPU, GPU, ANE, DRAM, SSD, Battery); universal bank-based key mapping for all Apple Silicon generations (M1–M5+, including Ultra dual-die); stale value caching with ~ indicator when sensors temporarily read invalid
  • Memory β€” used/total GB via host_statistics64 Mach API
  • Display β€” brightness, panel class (SDR/HDR/XDR), refresh rate / ProMotion, live HDR-active indicator from AppleARMBacklight IOReport DPB factor, plus IOReport SoC display controller and external display power via DISPEXT
  • Keyboard β€” backlight brightness and estimated power via IORegistry PWM
  • Battery β€” voltage, amperage, charge %, time remaining, temperature, drain/charge rate
  • SSD β€” model, interconnect (Apple Fabric/PCIe), power estimation from IORegistry disk counters
  • Peripherals β€” Thunderbolt/PCIe (IOReport measured), Ethernet (link speed, per-interface traffic), WiFi (signal/mode/channel, per-interface traffic), Bluetooth devices with battery levels, USB devices (speed/power/I/O counters)
  • Per-process energy β€” dynamically-sized top processes by session energy (from proc_pid_rusage), with per-process disk I/O rates, network traffic (via nettop), RAM footprint, dead process detection
  • Fans β€” RPM and cubic power model per fan
  • Collapsible tree β€” fold/unfold with arrows, +/- for all
  • Sparkline charts β€” pin any resource with Space, inline 1-line history column at wide terminals
  • Time-based SMA β€” toggle 0s/5s/10s smoothing window
  • Latency control β€” toggle UI refresh rate: 500ms / 2s / 5s
  • Mouse support β€” click to select rows
  • JSON mode β€” pipe structured data for scripts and dashboards
  • No sudo β€” runs entirely with user-level permissions

Install

With cargo

cargo install macpow

From source

git clone https://github.com/k06a/macpow.git
cd macpow
cargo build --release
./target/release/macpow

Homebrew

brew tap k06a/tap
brew install macpow

With pixi (conda-forge)

pixi global install macpow
# execute without installing
pixi exec macpow

Usage

macpow                    # TUI mode (default)
macpow --json             # JSON output to stdout
macpow --interval 500     # Set sampling interval in ms (default: 250)
macpow --dump             # Dump IOReport channel names (diagnostics)
macpow --dump-smc         # Dump every SMC key with type and value (diagnostics)

Keybindings

KeyAction
q / EscQuit
Up / Down / j / kMove cursor
Left / Right / hCollapse / expand tree node
+ / =Expand all nodes
-Collapse all nodes
SpacePin/unpin resource chart
aCycle SMA window: 0s / 5s / 10s
lCycle refresh interval: 250ms / 500ms / 1s / 2s
rReset all totals and min/max
PgUp / PgDnScroll by 10 rows
HomeJump to top
Mouse clickSelect row

All letter keys work on any keyboard layout (QWERTY, Russian, Dvorak, etc).

Architecture

Each data source runs in its own thread, updating shared metrics at its own pace. The TUI renders at the configured interval without blocking on slow sources.

+------------------+---------------------------------------------+
| Data source      | What it provides                            |
+------------------+---------------------------------------------+
| IOReport         | SoC power (Energy Model),                   |
|                  | CPU/GPU frequencies (DVFS residency)        |
| SMC              | System power (PSTR), display backlight     |
|                  | (PBwo on M5/Neo, PDBR on M1-M4),         |
|                  | adapter (PDTR), WiFi (wiPm), temps, fans   |
| IORegistry       | Battery, display brightness, keyboard PWM,  |
|                  | USB devices, SSD model, disk I/O counters   |
| CoreAudio        | Volume level, mute state                    |
| Mach API         | Per-CPU utilization ticks, memory stats      |
| proc_pid_rusage  | Per-process billed energy                   |
| getifaddrs       | Network traffic byte counters               |
| CoreWLAN/pmset   | WiFi info, Bluetooth devices                |
| IOPMAssertions   | Power assertions, audio playback detection  |
+------------------+---------------------------------------------+

Power measurements vs estimates

ComponentSourceMethod
CPU, GPU, ANE, DRAMIOReportDirect energy measurement (mJ/uJ/nJ deltas)
Media Engine, Camera (ISP)IOReportDirect energy measurement (AVE + MSR, ISP)
Fabric (AMCC, DCS, FAB, AFR)IOReportDirect energy measurement
Thunderbolt/PCIeIOReportDirect energy measurement (PCIe ports + controllers)
Display backlightSMC PBwo (M5 Pro/Max/Neo, A18) / PDBR (M1-M4 XDR)Direct power rail measurement
Display controllerIOReport DISP/DISPEXTDirect energy measurement (SoC + external)
Power adapterSMC PDTRDirect power delivery measurement
System totalSMC PSTRDirect power rail measurement
BatteryIORegistryV * I calculation
Per-processKernelri_billed_energy from rusage_info_v4
Per-process disk I/OKernelri_diskio_bytesread/written from rusage_info_v4
Per-process memoryKernelri_phys_footprint from rusage_info_v4
Per-process networknettopCumulative bytes per process (~18ms subprocess)
CPU utilizationMach APIhost_processor_info tick deltas
MemoryMach APIhost_statistics64 (active + inactive + wired + compressor pages)
KeyboardIORegistry PWMDuty cycle * 0.5W max
FansSMC RPMCubic model: (RPM/RPM_max)^3 * 1W
AudioCoreAudio + IOPMAssertionsIdle 0.05W + volume^2 * 1W
WiFiSMC wiPmDirect power measurement
BluetoothpmsetFixed per device type (0.01-0.05W)
SSDIORegistry countersI/O utilization: 0.03-2.5W
EthernetgetifaddrsLink detection, speed, per-interface traffic (data only)
NetworkgetifaddrsPer-interface byte counters for Ethernet and WiFi
USBIORegistry PowerOutDetailsPer-port power measurement (Watts/PDPowermW)

IOReport channel naming (multi-die support)

IOReport channel names vary between single-die and multi-die (Ultra) chips. The parser handles both generically:

Single-die (M1/M2/M3/M4 base/Pro/Max):
  CPU Stats:    ECPU0, PCPU10             ← digit suffix
  Energy Model: EACC_CPU0, PACC0_CPU5     ← _CPU suffix
  Blocks:       ISP, DRAM, ANE, DISP      ← bare names

Multi-die (M1/M2/M3 Ultra):
  CPU Stats:    DIE_0_ECPU_CPU0, DIE_1_PCPU1_CPU3   ← DIE_N_ prefix + _CPU suffix
  Energy Model: DIE_0_EACC_CPU0, DIE_1_PACC1_CPU3   ← DIE_N_ prefix + _CPU suffix
  Blocks:       ISP0_0, DRAM0_1, ANE0_0              ← per-die suffix

Two design rules keep this forward-compatible with future chips:

  1. strip_die_prefix generically removes DIE_{N}_ so downstream parsers always see the same base format
  2. starts_with matching for block power handles any suffix Apple may add (e.g. ISP matches ISP, ISP0_0, ISP0_1, etc.)

If a new chip isn't detected correctly, run macpow --dump to see the raw IOReport channel names.

Requirements

  • macOS 12+ (Monterey or later)
  • Apple Silicon (M1, M2, M3, M4, M5 β€” any variant)
  • Rust 1.70+

Release checklist

Toolchain pinning. The repo ships a rust-toolchain.toml that pins stable, matching dtolnay/rust-toolchain@stable in CI. Run rustup update stable before each release so local clippy/fmt see the same lints CI does β€” newly-promoted clippy lints have caused CI failures after cargo publish already uploaded the broken version (immutable), so this step is non-optional.

# 0. Make sure the toolchain matches CI exactly
rustup update stable
rustc --version    # note the version; CI is on the same `stable`

# 1. Bump version
vim Cargo.toml                        # update version = "X.Y.Z"

# 2. Build, lint, test β€” must all pass before the version bump commit
cargo fmt --check                     # formatting clean
# Clippy: copy this command verbatim from .github/workflows/ci.yml.
# CI does NOT pass --all-targets, so neither do we β€” adding it pulls in
# benches/examples that CI ignores and would block the release.
cargo clippy -- -D warnings -A clippy::field_reassign_with_default -A clippy::manual_c_str_literals -A clippy::manual_clamp -A clippy::manual_range_contains -A clippy::missing_safety_doc -A clippy::needless_range_loop
cargo test                            # all tests pass
cargo build --release                 # final binary build

# 3. Dry-run the publish so any cargo-side issues show up before tagging
cargo publish --dry-run

# 4. Update Homebrew badge in README.md
#    Change: homebrew-vX.Y.Z in the badge URL

# 5. Commit, tag, push
git add -A
git commit -m "Bump version to X.Y.Z"
git tag vX.Y.Z
git push origin main --tags
# CI will auto-create GitHub Release with binary

# 6. Publish to crates.io  (immutable β€” only after CI is green!)
cargo publish

# 7. Update Homebrew tap (via PR to trigger bottle building)
curl -sL https://github.com/k06a/macpow/archive/refs/tags/vX.Y.Z.tar.gz | shasum -a 256
# Update url + sha256 in homebrew-tap/Formula/macpow.rb
cd ../homebrew-tap
git checkout main && git pull
git checkout -b update-macpow-X.Y.Z
# edit Formula/macpow.rb (url + sha256 only β€” bottle block is auto-rewritten)
git commit -am "Update macpow to X.Y.Z"
git push origin update-macpow-X.Y.Z
# Create PR, wait for CI to build bottles, then add label "pr-pull"
# publish.yml will upload bottles and merge the PR

# 8. Update conda-forge feedstock
cd ../macpow-feedstock
git checkout main && git pull origin main
git checkout -b bump-X.Y.Z
# edit recipe/recipe.yaml: bump `version:` and update `sha256:` (same value as step 7)
git commit -am "macpow vX.Y.Z"
git push fork bump-X.Y.Z   # fork remote, since origin is conda-forge upstream
# Open PR against conda-forge/macpow-feedstock from your fork branch

If CI fails AFTER you ran cargo publish (step 6), that crates.io version is immutable. Yank it (cargo yank --version X.Y.Z) and bump the patch number β€” don't try to force-push the tag.

Contributing

Before submitting a PR, please run:

cargo fmt --check          # code formatting
# Clippy: must match CI (see .github/workflows/ci.yml)
cargo clippy -- -D warnings -A clippy::field_reassign_with_default -A clippy::manual_c_str_literals -A clippy::manual_clamp -A clippy::manual_range_contains -A clippy::missing_safety_doc -A clippy::needless_range_loop
cargo test                 # unit + integration tests
cargo build --release      # final build check

All four must pass with zero errors. Clippy needs the flags above so local runs match CI; without them, stable Clippy may report project-wide warnings that CI intentionally allows (FFI / Objective-C style).

Collecting diagnostics

If per-core temperatures are missing or incorrect on your Mac, please open an issue with:

macpow --dump > dump.txt                       # IOReport channel names
macpow --dump-smc > smc.txt                    # all SMC keys (incl. power rails)
macpow --json > metrics.json                   # full metrics (Ctrl+C after ~15s)
system_profiler SPHardwareDataType | head -10   # chip model

This helps add support for new Apple Silicon variants.

License

MIT