Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

collector/cpu: Support CPU online status #3032

Merged
merged 2 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 51 additions & 10 deletions collector/cpu_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package collector

import (
"errors"
"fmt"
"log/slog"
"os"
Expand All @@ -26,15 +27,17 @@ import (
"strconv"
"sync"

"golang.org/x/exp/maps"

"github.com/alecthomas/kingpin/v2"
"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
Expand All @@ -45,6 +48,7 @@ type cpuCollector struct {
cpuPackageThrottle *prometheus.Desc
cpuIsolated *prometheus.Desc
logger *slog.Logger
cpuOnline *prometheus.Desc
cpuStats map[int64]procfs.CPUStat
cpuStatsMutex sync.Mutex
isolatedCpus []uint16
Expand All @@ -70,17 +74,17 @@ func init() {

// NewCPUCollector returns a new Collector exposing kernel/system statistics.
func NewCPUCollector(logger *slog.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)
Expand All @@ -89,8 +93,9 @@ func NewCPUCollector(logger *slog.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.",
Expand Down Expand Up @@ -131,6 +136,11 @@ func NewCPUCollector(logger *slog.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),
Expand Down Expand Up @@ -177,12 +187,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
}
Expand Down Expand Up @@ -333,9 +352,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
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ require (
github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.61.0
github.com/prometheus/exporter-toolkit v0.13.2
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.5.9
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
golang.org/x/sys v0.28.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFS
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
github.com/prometheus/exporter-toolkit v0.13.2 h1:Z02fYtbqTMy2i/f+xZ+UK5jy/bl1Ex3ndzh06T/Q9DQ=
github.com/prometheus/exporter-toolkit v0.13.2/go.mod h1:tCqnfx21q6qN1KA4U3Bfb8uWzXfijIrJz3/kTIqMV7g=
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.5.9 h1://6RvaOKFf3nQ0rl5+8zBbE4/72455VC9Jq61pfq67E=
Expand Down
Loading