Two-socket machines: Hardware and Top show each CPU, not one with every core
Hardware counts CPUs, cores and threads per CPU. Top groups threads under a row per socket, reads each core's temperature from its own socket's coretemp chip, and the CPU temperature is the hotter package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
4b238ea93e
commit
1e2dcacb2c
@@ -60,6 +60,7 @@ type Temp struct {
|
||||
C float64 `json:"c"`
|
||||
Max float64 `json:"max,omitempty"`
|
||||
Crit float64 `json:"crit,omitempty"`
|
||||
Pkg int `json:"pkg,omitempty"` // coretemp: the CPU package the chip belongs to — a two-socket board has two chips, both with a "Core 0"
|
||||
}
|
||||
|
||||
type Fan struct {
|
||||
@@ -544,6 +545,19 @@ func hwmonDir(d string) (chip string, temps []Temp, fans []Fan) {
|
||||
fans = append(fans, Fan{Chip: chip, Label: label, RPM: int(raw)})
|
||||
}
|
||||
}
|
||||
// coretemp is one chip per CPU package, and only its "Package id N" says which
|
||||
if chip == "coretemp" {
|
||||
for _, t := range temps {
|
||||
if n, ok := strings.CutPrefix(t.Label, "Package id "); ok {
|
||||
if pkg, err := strconv.Atoi(n); err == nil {
|
||||
for i := range temps {
|
||||
temps[i].Pkg = pkg
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -559,7 +573,10 @@ func hwmon() ([]Temp, []Fan) {
|
||||
if temps[i].Chip != temps[j].Chip {
|
||||
return temps[i].Chip < temps[j].Chip
|
||||
}
|
||||
return temps[i].Label < temps[j].Label
|
||||
if temps[i].Label != temps[j].Label {
|
||||
return temps[i].Label < temps[j].Label
|
||||
}
|
||||
return temps[i].Pkg < temps[j].Pkg
|
||||
})
|
||||
return temps, fans
|
||||
}
|
||||
@@ -1112,6 +1129,38 @@ func coreIDs(n int) []int {
|
||||
return coreIDsVal[:n]
|
||||
}
|
||||
|
||||
// corePkgs maps logical CPUs to the CPU package (socket) each is on, and is nil on a
|
||||
// one-socket machine: there is nothing to tell apart.
|
||||
var (
|
||||
corePkgsOnce sync.Once
|
||||
corePkgsVal []int
|
||||
)
|
||||
|
||||
func corePkgs(n int) []int {
|
||||
corePkgsOnce.Do(func() {
|
||||
seen := map[int]bool{}
|
||||
var out []int
|
||||
for i := 0; ; i++ {
|
||||
b, err := os.ReadFile("/sys/devices/system/cpu/cpu" + strconv.Itoa(i) + "/topology/physical_package_id")
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
id, err := strconv.Atoi(strings.TrimSpace(string(b)))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
out, seen[id] = append(out, id), true
|
||||
}
|
||||
if len(seen) > 1 {
|
||||
corePkgsVal = out
|
||||
}
|
||||
})
|
||||
if len(corePkgsVal) < n {
|
||||
return nil
|
||||
}
|
||||
return corePkgsVal[:n]
|
||||
}
|
||||
|
||||
// ProcTable is the Processes tab payload.
|
||||
type ProcTable struct {
|
||||
T int64 `json:"t"`
|
||||
@@ -1123,7 +1172,8 @@ type ProcTable struct {
|
||||
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)
|
||||
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"`
|
||||
@@ -1182,18 +1232,24 @@ func cpuMHz() float64 {
|
||||
|
||||
// cpuTemp picks the sensor that best represents the CPU package.
|
||||
func cpuTemp(temps []Temp) float64 {
|
||||
best, max := 0.0, 0.0
|
||||
// the hottest package: a two-socket board has two, and the hotter one is the one to watch
|
||||
pkg, max := 0.0, 0.0
|
||||
for _, t := range temps {
|
||||
l := strings.ToLower(t.Label)
|
||||
if t.Chip == "coretemp" && strings.HasPrefix(l, "package") || t.Chip == "k10temp" && (l == "tctl" || l == "tdie") ||
|
||||
t.Chip == "thermal" && strings.Contains(l, "cpu") || t.Chip == "cpu_thermal" || t.Chip == "soc_thermal" {
|
||||
return t.C
|
||||
if t.C > pkg {
|
||||
pkg = t.C
|
||||
}
|
||||
}
|
||||
if (t.Chip == "coretemp" || t.Chip == "k10temp") && t.C > max {
|
||||
max, best = t.C, t.C
|
||||
max = t.C
|
||||
}
|
||||
}
|
||||
return best
|
||||
if pkg > 0 {
|
||||
return pkg
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
var bootTime = func() int64 {
|
||||
@@ -1323,6 +1379,7 @@ func (c *Collector) Processes() ProcTable {
|
||||
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.CoreIDs = coreIDs(len(last.Cores))
|
||||
t.CorePkgs = corePkgs(len(last.Cores))
|
||||
from := n - 90
|
||||
if from < 0 {
|
||||
from = 0
|
||||
|
||||
Reference in New Issue
Block a user