From a20efb6181c3ac43234981d1683744a73d16bea9 Mon Sep 17 00:00:00 2001 From: Pranshu Srivastava Date: Thu, 30 May 2024 13:00:31 +0530 Subject: [PATCH] collector/cpu: Support CPU online status Blocked by: https://github.com/prometheus/procfs/pull/644. Signed-off-by: Pranshu Srivastava --- collector/cpu_linux.go | 61 +++++++++++++++++++++++++++++++++++------- go.mod | 4 ++- go.sum | 4 +-- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/collector/cpu_linux.go b/collector/cpu_linux.go index 75d2a795b2..34172ec442 100644 --- a/collector/cpu_linux.go +++ b/collector/cpu_linux.go @@ -17,6 +17,7 @@ package collector import ( + "errors" "fmt" "os" "path/filepath" @@ -25,17 +26,19 @@ import ( "strconv" "sync" + "golang.org/x/exp/maps" + "github.com/alecthomas/kingpin/v2" "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" "github.com/prometheus/procfs/sysfs" - "golang.org/x/exp/maps" ) type cpuCollector struct { - fs procfs.FS + procfs procfs.FS + sysfs sysfs.FS cpu *prometheus.Desc cpuInfo *prometheus.Desc cpuFrequencyHz *prometheus.Desc @@ -45,6 +48,7 @@ type cpuCollector struct { cpuCoreThrottle *prometheus.Desc cpuPackageThrottle *prometheus.Desc cpuIsolated *prometheus.Desc + cpuOnline *prometheus.Desc logger log.Logger cpuStats map[int64]procfs.CPUStat cpuStatsMutex sync.Mutex @@ -71,17 +75,17 @@ func init() { // NewCPUCollector returns a new Collector exposing kernel/system statistics. func NewCPUCollector(logger log.Logger) (Collector, error) { - fs, err := procfs.NewFS(*procPath) + pfs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) } - sysfs, err := sysfs.NewFS(*sysPath) + sfs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) } - isolcpus, err := sysfs.IsolatedCPUs() + isolcpus, err := sfs.IsolatedCPUs() if err != nil { if !os.IsNotExist(err) { return nil, fmt.Errorf("Unable to get isolated cpus: %w", err) @@ -90,8 +94,9 @@ func NewCPUCollector(logger log.Logger) (Collector, error) { } c := &cpuCollector{ - fs: fs, - cpu: nodeCPUSecondsDesc, + procfs: pfs, + sysfs: sfs, + cpu: nodeCPUSecondsDesc, cpuInfo: prometheus.NewDesc( prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "info"), "CPU information from /proc/cpuinfo.", @@ -132,6 +137,11 @@ func NewCPUCollector(logger log.Logger) (Collector, error) { "Whether each core is isolated, information from /sys/devices/system/cpu/isolated.", []string{"cpu"}, nil, ), + cpuOnline: prometheus.NewDesc( + prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "online"), + "CPUs that are online and being scheduled.", + []string{"cpu"}, nil, + ), logger: logger, isolatedCpus: isolcpus, cpuStats: make(map[int64]procfs.CPUStat), @@ -178,12 +188,21 @@ func (c *cpuCollector) Update(ch chan<- prometheus.Metric) error { if c.isolatedCpus != nil { c.updateIsolated(ch) } - return c.updateThermalThrottle(ch) + err := c.updateThermalThrottle(ch) + if err != nil { + return err + } + err = c.updateOnline(ch) + if err != nil { + return err + } + + return nil } // updateInfo reads /proc/cpuinfo func (c *cpuCollector) updateInfo(ch chan<- prometheus.Metric) error { - info, err := c.fs.CPUInfo() + info, err := c.procfs.CPUInfo() if err != nil { return err } @@ -334,9 +353,31 @@ func (c *cpuCollector) updateIsolated(ch chan<- prometheus.Metric) { } } +// updateOnline reads /sys/devices/system/cpu/cpu*/online through sysfs and exports online status metrics. +func (c *cpuCollector) updateOnline(ch chan<- prometheus.Metric) error { + cpus, err := c.sysfs.CPUs() + if err != nil { + return err + } + // No-op if the system does not support CPU online stats. + cpu0 := cpus[0] + if _, err := cpu0.Online(); err != nil && errors.Is(err, os.ErrNotExist) { + return nil + } + for _, cpu := range cpus { + setOnline := float64(0) + if online, _ := cpu.Online(); online { + setOnline = 1 + } + ch <- prometheus.MustNewConstMetric(c.cpuOnline, prometheus.GaugeValue, setOnline, cpu.Number()) + } + + return nil +} + // updateStat reads /proc/stat through procfs and exports CPU-related metrics. func (c *cpuCollector) updateStat(ch chan<- prometheus.Metric) error { - stats, err := c.fs.Stat() + stats, err := c.procfs.Stat() if err != nil { return err } diff --git a/go.mod b/go.mod index 4c1a572be3..1f2a1dea4a 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.55.0 github.com/prometheus/exporter-toolkit v0.11.0 - github.com/prometheus/procfs v0.15.1 + github.com/prometheus/procfs v0.15.2-0.20240603130017-1754b780536b // == v0.15.1 + https://github.com/prometheus/procfs/commit/1754b780536bb81082baa913e04cc4fff4d2baea github.com/safchain/ethtool v0.4.1 golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f golang.org/x/sys v0.22.0 @@ -58,3 +58,5 @@ require ( google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) + +replace github.com/rexagod/procfs => ../procfs // https://github.com/prometheus/procfs/pull/644 diff --git a/go.sum b/go.sum index bde5ad21f5..3988745d36 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g= github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.15.2-0.20240603130017-1754b780536b h1:4EJkx3vycI+n5JY5ht+bnSUGamkmmXkpcNeO/OBT/0A= +github.com/prometheus/procfs v0.15.2-0.20240603130017-1754b780536b/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/safchain/ethtool v0.4.1 h1:S6mEleTADqgynileXoiapt/nKnatyR6bmIHoF+h2ADo=