// Copyright 2018 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build !windows package sysfs import ( "os" "path/filepath" "strings" "golang.org/x/sync/errgroup" "github.com/prometheus/procfs/internal/util" ) // CPU represents a path to a CPU located in /sys/devices/system/cpu/cpu[0-9]* type CPU string // Number returns the ID number of the given CPU func (c CPU) Number() string { return strings.TrimPrefix(filepath.Base(string(c)), "cpu") } // CPUTopology contains data located in /sys/devices/system/cpu/cpu[0-9]*/topology type CPUTopology struct { CoreID string CoreSiblingsList string PhysicalPackageID string ThreadSiblingsList string } // CPUThermalThrottle contains data from /sys/devices/system/cpu/cpu[0-9]*/thermal_throttle type CPUThermalThrottle struct { CoreThrottleCount uint64 PackageThrottleCount uint64 } // SystemCPUCpufreqStats contains stats from devices/system/cpu/cpu[0-9]*/cpufreq/... type SystemCPUCpufreqStats struct { Name string CpuinfoCurrentFrequency *uint64 CpuinfoMinimumFrequency *uint64 CpuinfoMaximumFrequency *uint64 CpuinfoTransitionLatency *uint64 ScalingCurrentFrequency *uint64 ScalingMinimumFrequency *uint64 ScalingMaximumFrequency *uint64 AvailableGovernors string Driver string Governor string RelatedCpus string SetSpeed string } // CPUs returns a slice of all CPUs in /sys/devices/system/cpu func (fs FS) CPUs() ([]CPU, error) { cpuPaths, err := filepath.Glob(fs.sys.Path("devices/system/cpu/cpu[0-9]*")) if err != nil { return nil, err } cpus := make([]CPU, len(cpuPaths)) for i, cpu := range cpuPaths { cpus[i] = CPU(cpu) } return cpus, nil } // Topology gets the topology information for a single CPU from /sys/devices/system/cpu/cpuN/topology func (c CPU) Topology() (*CPUTopology, error) { cpuTopologyPath := filepath.Join(string(c), "topology") if _, err := os.Stat(cpuTopologyPath); err != nil { return nil, err } t, err := parseCPUTopology(cpuTopologyPath) if err != nil { return nil, err } return t, nil } func parseCPUTopology(cpuPath string) (*CPUTopology, error) { t := CPUTopology{} var err error t.CoreID, err = util.SysReadFile(filepath.Join(cpuPath, "core_id")) if err != nil { return nil, err } t.PhysicalPackageID, err = util.SysReadFile(filepath.Join(cpuPath, "physical_package_id")) if err != nil { return nil, err } t.CoreSiblingsList, err = util.SysReadFile(filepath.Join(cpuPath, "core_siblings_list")) if err != nil { return nil, err } t.ThreadSiblingsList, err = util.SysReadFile(filepath.Join(cpuPath, "thread_siblings_list")) if err != nil { return nil, err } return &t, nil } // ThermalThrottle gets the cpu throttle count information for a single CPU from /sys/devices/system/cpu/cpuN/thermal_throttle func (c CPU) ThermalThrottle() (*CPUThermalThrottle, error) { cpuPath := filepath.Join(string(c), "thermal_throttle") if _, err := os.Stat(cpuPath); err != nil { return nil, err } t, err := parseCPUThermalThrottle(cpuPath) if err != nil { return nil, err } return t, nil } func parseCPUThermalThrottle(cpuPath string) (*CPUThermalThrottle, error) { t := CPUThermalThrottle{} var err error t.PackageThrottleCount, err = util.ReadUintFromFile(filepath.Join(cpuPath, "package_throttle_count")) if err != nil { return nil, err } t.CoreThrottleCount, err = util.ReadUintFromFile(filepath.Join(cpuPath, "core_throttle_count")) if err != nil { return nil, err } return &t, nil } // SystemCpufreq returns CPU frequency metrics for all CPUs. func (fs FS) SystemCpufreq() ([]SystemCPUCpufreqStats, error) { var g errgroup.Group cpus, err := filepath.Glob(fs.sys.Path("devices/system/cpu/cpu[0-9]*")) if err != nil { return nil, err } systemCpufreq := make([]SystemCPUCpufreqStats, len(cpus)) for i, cpu := range cpus { cpuName := strings.TrimPrefix(filepath.Base(cpu), "cpu") cpuCpufreqPath := filepath.Join(cpu, "cpufreq") _, err = os.Stat(cpuCpufreqPath) if os.IsNotExist(err) { continue } else if err != nil { return nil, err } // Execute the parsing of each CPU in parallel. // This is done because the kernel intentionally delays access to each CPU by // 50 milliseconds to avoid DDoSing possibly expensive functions. i := i // https://golang.org/doc/faq#closures_and_goroutines g.Go(func() error { cpufreq, err := parseCpufreqCpuinfo(cpuCpufreqPath) if err == nil { cpufreq.Name = cpuName systemCpufreq[i] = *cpufreq } return err }) } if err = g.Wait(); err != nil { return nil, err } return systemCpufreq, nil } func parseCpufreqCpuinfo(cpuPath string) (*SystemCPUCpufreqStats, error) { uintFiles := []string{ "cpuinfo_cur_freq", "cpuinfo_max_freq", "cpuinfo_min_freq", "cpuinfo_transition_latency", "scaling_cur_freq", "scaling_max_freq", "scaling_min_freq", } uintOut := make([]*uint64, len(uintFiles)) for i, f := range uintFiles { v, err := util.ReadUintFromFile(filepath.Join(cpuPath, f)) if err != nil { if os.IsNotExist(err) || os.IsPermission(err) { continue } return &SystemCPUCpufreqStats{}, err } uintOut[i] = &v } stringFiles := []string{ "scaling_available_governors", "scaling_driver", "scaling_governor", "related_cpus", "scaling_setspeed", } stringOut := make([]string, len(stringFiles)) var err error for i, f := range stringFiles { stringOut[i], err = util.SysReadFile(filepath.Join(cpuPath, f)) if err != nil { return &SystemCPUCpufreqStats{}, err } } return &SystemCPUCpufreqStats{ CpuinfoCurrentFrequency: uintOut[0], CpuinfoMaximumFrequency: uintOut[1], CpuinfoMinimumFrequency: uintOut[2], CpuinfoTransitionLatency: uintOut[3], ScalingCurrentFrequency: uintOut[4], ScalingMaximumFrequency: uintOut[5], ScalingMinimumFrequency: uintOut[6], AvailableGovernors: stringOut[0], Driver: stringOut[1], Governor: stringOut[2], RelatedCpus: stringOut[3], SetSpeed: stringOut[4], }, nil }