1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
// 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
}
|