Top: the second CPU's core temperatures on a two-socket machine

The sensor feed dropped which socket a coretemp reading came from, so every core matched
the first CPU's chip. coretemp sensors now carry a package label, and history folding
keeps the two sockets' same-named cores apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-11-10 00:22:05 -08:00
co-authored by Claude Opus 5.5
parent a516cf4cad
commit f1c66ce886
3 changed files with 10 additions and 4 deletions
+4 -2
View File
@@ -1,5 +1,7 @@
package metrics package metrics
import "strconv"
// One kept history point is the average of the samples behind it. At the default ten-minute // One kept history point is the average of the samples behind it. At the default ten-minute
// window that bucket is a single sample and nothing is averaged; at a day it is five minutes // window that bucket is a single sample and nothing is averaged; at a day it is five minutes
// of them, and the difference matters — a graph of the last day drawn from whichever instant // of them, and the difference matters — a graph of the last day drawn from whichever instant
@@ -85,13 +87,13 @@ func fold(b []Sample) Sample {
temps := avgBy(b, func(s Sample) map[string][]float64 { temps := avgBy(b, func(s Sample) map[string][]float64 {
m := make(map[string][]float64, len(s.Temps)) m := make(map[string][]float64, len(s.Temps))
for _, x := range s.Temps { for _, x := range s.Temps {
m[x.Chip+"/"+x.Label] = []float64{x.C} m[x.Chip+"/"+x.Label+"/"+strconv.Itoa(x.Pkg)] = []float64{x.C}
} }
return m return m
}) })
out.Temps = append([]Temp(nil), out.Temps...) out.Temps = append([]Temp(nil), out.Temps...)
for i, x := range out.Temps { for i, x := range out.Temps {
if v, ok := temps[x.Chip+"/"+x.Label]; ok { if v, ok := temps[x.Chip+"/"+x.Label+"/"+strconv.Itoa(x.Pkg)]; ok {
out.Temps[i].C = v[0] out.Temps[i].C = v[0]
} }
} }
+5 -1
View File
@@ -96,7 +96,11 @@ func flatten(s Sample, want func(id string) bool) []sensors.Sensor {
if x.Crit > 0 { if x.Crit > 0 {
b.crit = sensors.F(x.Crit) b.crit = sensors.F(x.Crit)
} }
f.add(sensors.ID("temp", sensors.Seg(x.Chip), sensors.Seg(x.Label)), x.Chip+" "+x.Label, "temperature", "°C", x.C, t, b, "chip", x.Chip, "label", x.Label) labels := []string{"chip", x.Chip, "label", x.Label}
if x.Chip == "coretemp" { // a two-socket board has a coretemp chip per CPU, both with a "Core 0"
labels = append(labels, "package", strconv.Itoa(x.Pkg))
}
f.add(sensors.ID("temp", sensors.Seg(x.Chip), sensors.Seg(x.Label)), x.Chip+" "+x.Label, "temperature", "°C", x.C, t, b, labels...)
} }
for _, x := range s.Fans { for _, x := range s.Fans {
f.add(sensors.ID("fan", sensors.Seg(x.Chip), sensors.Seg(x.Label)), x.Chip+" "+x.Label, "fan", "rpm", float64(x.RPM), t, none, "chip", x.Chip, "label", x.Label) f.add(sensors.ID("fan", sensors.Seg(x.Chip), sensors.Seg(x.Label)), x.Chip+" "+x.Label, "fan", "rpm", float64(x.RPM), t, none, "chip", x.Chip, "label", x.Label)
+1 -1
View File
@@ -47,7 +47,7 @@ export function sampleOf(v: Values, cat: Catalog): Sample {
t: Date.now(), interval_ms: num(v, 'hamlab.sample_interval'), t: Date.now(), interval_ms: num(v, 'hamlab.sample_interval'),
cpu: num(v, 'cpu.usage'), cores, load: [num(v, 'load.1'), num(v, 'load.5'), num(v, 'load.15')], cpu: num(v, 'cpu.usage'), cores, load: [num(v, 'load.1'), num(v, 'load.5'), num(v, 'load.15')],
mem: mem('mem'), swap: mem('swap'), uptime: num(v, 'uptime'), mem: mem('mem'), swap: mem('swap'), uptime: num(v, 'uptime'),
temps: hw('temp.').map(({ id, s }) => ({ chip: s.labels!.chip, label: s.labels!.label, c: num(v, id), max: s.warn, crit: s.crit })), temps: hw('temp.').map(({ id, s }) => ({ chip: s.labels!.chip, label: s.labels!.label, c: num(v, id), max: s.warn, crit: s.crit, pkg: s.labels!.package !== undefined ? Number(s.labels!.package) : undefined })),
fans: hw('fan.').map(({ id, s }) => ({ chip: s.labels!.chip, label: s.labels!.label, rpm: num(v, id) })), fans: hw('fan.').map(({ id, s }) => ({ chip: s.labels!.chip, label: s.labels!.label, rpm: num(v, id) })),
net: [...groups(v, cat, 'net.', 'iface')].map(([iface, p]) => ({ iface, up: v[p + '.up'] === true, rx_bps: num(v, p + '.rx'), tx_bps: num(v, p + '.tx'), rx_total: num(v, p + '.rx_total'), tx_total: num(v, p + '.tx_total') })), net: [...groups(v, cat, 'net.', 'iface')].map(([iface, p]) => ({ iface, up: v[p + '.up'] === true, rx_bps: num(v, p + '.rx'), tx_bps: num(v, p + '.tx'), rx_total: num(v, p + '.rx_total'), tx_total: num(v, p + '.tx_total') })),
disks: [...groups(v, cat, 'disk.', 'device')].map(([name, p]) => ({ name, model: cat.get(p + '.size')?.labels?.model || undefined, size: num(v, p + '.size'), read_bps: num(v, p + '.read'), write_bps: num(v, p + '.write'), util_pct: num(v, p + '.util'), temp: num(v, p + '.temp') || undefined })), disks: [...groups(v, cat, 'disk.', 'device')].map(([name, p]) => ({ name, model: cat.get(p + '.size')?.labels?.model || undefined, size: num(v, p + '.size'), read_bps: num(v, p + '.read'), write_bps: num(v, p + '.write'), util_pct: num(v, p + '.util'), temp: num(v, p + '.temp') || undefined })),