110 lines
2.1 KiB
Go

package collector
import (
"os/exec"
"strconv"
"strings"
"time"
)
type CPUInfo struct {
Model string `json:"model"`
Cores int `json:"cores"`
Threads int `json:"threads"`
FreqMHz int `json:"freq_mhz"`
UsagePercent float64 `json:"usage_percent"`
}
func CollectCPU() CPUInfo {
info := CPUInfo{}
// CPU Model
info.Model = sysctlGet("hw.model")
// Anzahl CPUs (Threads)
if v, err := strconv.Atoi(sysctlGet("hw.ncpu")); err == nil {
info.Threads = v
}
// Kerne - versuche kern.smp.cores, Fallback auf ncpu
if v, err := strconv.Atoi(sysctlGet("kern.smp.cores")); err == nil && v > 0 {
info.Cores = v
} else {
info.Cores = info.Threads
}
// Frequenz in MHz
if v, err := strconv.Atoi(sysctlGet("dev.cpu.0.freq")); err == nil {
info.FreqMHz = v
}
// CPU-Auslastung via kern.cp_time (zwei Messungen im Abstand von 1 Sekunde)
info.UsagePercent = measureCPUUsage()
return info
}
func sysctlGet(key string) string {
out, err := exec.Command("sysctl", "-n", key).Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
// measureCPUUsage misst die CPU-Auslastung ueber kern.cp_time
// Format: user nice sys intr idle
func measureCPUUsage() float64 {
parse := func() (idle, total int64, ok bool) {
out := sysctlGet("kern.cp_time")
if out == "" {
return 0, 0, false
}
fields := strings.Fields(out)
if len(fields) < 5 {
return 0, 0, false
}
var vals [5]int64
for i := 0; i < 5; i++ {
v, err := strconv.ParseInt(fields[i], 10, 64)
if err != nil {
return 0, 0, false
}
vals[i] = v
}
// user + nice + sys + intr + idle
total = vals[0] + vals[1] + vals[2] + vals[3] + vals[4]
idle = vals[4]
return idle, total, true
}
idle1, total1, ok1 := parse()
if !ok1 {
return 0
}
time.Sleep(1 * time.Second)
idle2, total2, ok2 := parse()
if !ok2 {
return 0
}
totalDelta := float64(total2 - total1)
if totalDelta == 0 {
return 0
}
idleDelta := float64(idle2 - idle1)
usage := (1.0 - idleDelta/totalDelta) * 100.0
if usage < 0 {
usage = 0
}
if usage > 100 {
usage = 100
}
return usage
}