// Copyright 2019 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. //go:build linux // +build linux package sysfs import ( "fmt" "os" "path/filepath" "strconv" "strings" "github.com/prometheus/procfs/internal/util" ) // RaplZone stores the information for one RAPL power zone. type RaplZone struct { Name string // name of RAPL zone from file "name" Index int // index (different value for duplicate names) Path string // filesystem path of RaplZone MaxMicrojoules uint64 // max RAPL microjoule value } // GetRaplZones returns a slice of RaplZones. When RAPL files are not present, // returns nil with error. // - https://www.kernel.org/doc/Documentation/power/powercap/powercap.txt func GetRaplZones(fs FS) ([]RaplZone, error) { raplDir := fs.sys.Path("class/powercap") files, err := os.ReadDir(raplDir) if err != nil { return nil, fmt.Errorf("unable to read class/powercap: %w", err) } var zones []RaplZone // Count name usages to avoid duplicates (label them with an index). countNameUsages := make(map[string]int) // Loop through directory files searching for file "name" from subdirs. for _, f := range files { nameFile := filepath.Join(raplDir, f.Name(), "/name") nameBytes, err := os.ReadFile(nameFile) if err == nil { // Add new rapl zone since name file was found. name := strings.TrimSpace(string(nameBytes)) // get a pair of index and final name index, name := getIndexAndName(countNameUsages, name) maxMicrojouleFilename := filepath.Join(raplDir, f.Name(), "/max_energy_range_uj") maxMicrojoules, err := util.ReadUintFromFile(maxMicrojouleFilename) if err != nil { return nil, err } zone := RaplZone{ Name: name, Index: index, Path: filepath.Join(raplDir, f.Name()), MaxMicrojoules: maxMicrojoules, } zones = append(zones, zone) // Store into map how many times this name has been used. There can // be e.g. multiple "dram" instances without any index postfix. The // count is then used for indexing countNameUsages[name] = index + 1 } } return zones, nil } // GetEnergyMicrojoules returns the current microjoule value from the zone energy counter // https://www.kernel.org/doc/Documentation/power/powercap/powercap.txt func (rz RaplZone) GetEnergyMicrojoules() (uint64, error) { return util.ReadUintFromFile(filepath.Join(rz.Path, "/energy_uj")) } // getIndexAndName returns a pair of (index, name) for a given name and name // counting map. Some RAPL-names have an index at the end, some have duplicates // without an index at the end. When the index is embedded in the name, it is // provided back as an integer, and stripped from the returned name. Usage // count is used when the index value is absent from the name. func getIndexAndName(countNameUsages map[string]int, name string) (int, string) { s := strings.Split(name, "-") if len(s) == 2 { index, err := strconv.Atoi(s[1]) if err == nil { return index, s[0] } } // return count as the index, since name didn't have an index at the end return countNameUsages[name], name }