Top: each CPU's own wattage on a two-socket machine, its threads folded under it

Per-package RAPL watts in the sample, the process table and as cpu.package.N.power
sensors. The socket rows collapse with an arrow; the fold is remembered per browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-11-10 00:07:24 -08:00
co-authored by Claude Opus 5.5
parent 1e2dcacb2c
commit e9a87af5e8
7 changed files with 131 additions and 62 deletions
+42 -40
View File
@@ -136,24 +136,25 @@ type Proc struct {
}
type Sample struct {
T int64 `json:"t"` // unix ms
Interval int64 `json:"interval_ms"` // sampling interval in effect
CPU float64 `json:"cpu"`
Cores []float64 `json:"cores"`
CPUPower float64 `json:"cpu_power_w,omitempty"` // the CPU packages' own energy counters; 0 = no reading
Load [3]float64 `json:"load"`
Mem Mem `json:"mem"`
Swap Mem `json:"swap"`
Temps []Temp `json:"temps"`
Fans []Fan `json:"fans"`
Net []Net `json:"net"`
Disks []Disk `json:"disks"`
FS []FS `json:"fs"`
UPS *UPS `json:"ups"`
GPUs []GPU `json:"gpus"`
ARC *ARC `json:"arc"` // the ZFS read cache; nil without the module
ZFSWrite *ZFSWrites `json:"zfs_writes"` // the ZFS write path; nil without the module
Uptime float64 `json:"uptime"`
T int64 `json:"t"` // unix ms
Interval int64 `json:"interval_ms"` // sampling interval in effect
CPU float64 `json:"cpu"`
Cores []float64 `json:"cores"`
CPUPower float64 `json:"cpu_power_w,omitempty"` // the CPU packages' own energy counters; 0 = no reading
CPUPowerPkgs []float64 `json:"cpu_power_pkgs_w,omitempty"` // each package's share, by socket; only with more than one
Load [3]float64 `json:"load"`
Mem Mem `json:"mem"`
Swap Mem `json:"swap"`
Temps []Temp `json:"temps"`
Fans []Fan `json:"fans"`
Net []Net `json:"net"`
Disks []Disk `json:"disks"`
FS []FS `json:"fs"`
UPS *UPS `json:"ups"`
GPUs []GPU `json:"gpus"`
ARC *ARC `json:"arc"` // the ZFS read cache; nil without the module
ZFSWrite *ZFSWrites `json:"zfs_writes"` // the ZFS write path; nil without the module
Uptime float64 `json:"uptime"`
fsT, upsT int64 // when the slow sections were really read; sensors report these, not T
}
@@ -350,7 +351,7 @@ func (c *Collector) sample() {
s := Sample{T: now.UnixMilli(), Interval: c.Interval().Milliseconds(), Temps: []Temp{}, Fans: []Fan{}, Net: []Net{}, Disks: []Disk{}}
s.CPU, s.Cores = c.cpu()
s.CPUPower = c.cpuPower(now)
s.CPUPower, s.CPUPowerPkgs = c.cpuPower(now)
s.Load = loadavg()
s.Mem, s.Swap = meminfo()
s.Uptime = uptime()
@@ -1163,26 +1164,27 @@ func corePkgs(n int) []int {
// ProcTable is the Processes tab payload.
type ProcTable struct {
T int64 `json:"t"`
CPU float64 `json:"cpu"`
Cores []float64 `json:"cores"`
CPUHist []float64 `json:"cpu_hist"` // recent total-CPU samples, oldest first
CPUModel string `json:"cpu_model,omitempty"`
CPUMHz float64 `json:"cpu_mhz,omitempty"`
CPUMaxMHz float64 `json:"cpu_max_mhz,omitempty"`
CPUPowerW float64 `json:"cpu_power_w,omitempty"`
CPUTemp float64 `json:"cpu_temp,omitempty"`
CoreIDs []int `json:"core_ids,omitempty"` // for each logical CPU, the physical core it is a thread of (what the temperature sensors are named by)
CorePkgs []int `json:"core_pkgs,omitempty"` // for each logical CPU, the socket it is on; only on a machine with more than one
Load [3]float64 `json:"load"`
Mem Mem `json:"mem"`
Swap Mem `json:"swap"`
Net []Net `json:"net"`
GPUs []GPU `json:"gpus"`
Uptime float64 `json:"uptime"`
Procs []Proc `json:"procs"`
Total int `json:"total"`
Running int `json:"running"`
T int64 `json:"t"`
CPU float64 `json:"cpu"`
Cores []float64 `json:"cores"`
CPUHist []float64 `json:"cpu_hist"` // recent total-CPU samples, oldest first
CPUModel string `json:"cpu_model,omitempty"`
CPUMHz float64 `json:"cpu_mhz,omitempty"`
CPUMaxMHz float64 `json:"cpu_max_mhz,omitempty"`
CPUPowerW float64 `json:"cpu_power_w,omitempty"`
CPUPowerPkgsW []float64 `json:"cpu_power_pkgs_w,omitempty"`
CPUTemp float64 `json:"cpu_temp,omitempty"`
CoreIDs []int `json:"core_ids,omitempty"` // for each logical CPU, the physical core it is a thread of (what the temperature sensors are named by)
CorePkgs []int `json:"core_pkgs,omitempty"` // for each logical CPU, the socket it is on; only on a machine with more than one
Load [3]float64 `json:"load"`
Mem Mem `json:"mem"`
Swap Mem `json:"swap"`
Net []Net `json:"net"`
GPUs []GPU `json:"gpus"`
Uptime float64 `json:"uptime"`
Procs []Proc `json:"procs"`
Total int `json:"total"`
Running int `json:"running"`
}
var cpuModel = func() string {
@@ -1377,7 +1379,7 @@ func (c *Collector) Processes() ProcTable {
t.Mem, t.Swap = meminfo()
if n := len(c.ring); n > 0 {
last := c.ring[n-1]
t.CPU, t.Cores, t.Net, t.CPUTemp, t.CPUPowerW = last.CPU, last.Cores, last.Net, cpuTemp(last.Temps), last.CPUPower
t.CPU, t.Cores, t.Net, t.CPUTemp, t.CPUPowerW, t.CPUPowerPkgsW = last.CPU, last.Cores, last.Net, cpuTemp(last.Temps), last.CPUPower, last.CPUPowerPkgs
t.CoreIDs = coreIDs(len(last.Cores))
t.CorePkgs = corePkgs(len(last.Cores))
from := n - 90