metric/system/cgroup/cgv2/cpu.go (77 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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.
package cgv2
import (
"bufio"
"fmt"
"os"
"path/filepath"
"github.com/elastic/elastic-agent-libs/opt"
"github.com/elastic/elastic-agent-system-metrics/metric/system/cgroup/cgcommon"
)
// CPUSubsystem contains metrics and limits from the "cpu" subsystem.
// in cgroupsV2, this merges both the 'cpu' and 'cpuacct' controllers.
type CPUSubsystem struct {
ID string `json:"id,omitempty"` // ID of the cgroup.
Path string `json:"path,omitempty"` // Path to the cgroup relative to the cgroup subsystem's mountpoint.
// Shows pressure stall information for CPU.
Pressure map[string]cgcommon.Pressure `json:"pressure,omitempty" struct:"pressure,omitempty"`
// Stats shows overall counters for the CPU controller
Stats CPUStats
}
// CPUStats carries the information from the cpu.stat cgroup file
type CPUStats struct {
//The following three metrics are only available when the controller is enabled.
Throttled ThrottledField `json:"throttled,omitempty" struct:"throttled,omitempty"`
Periods opt.Uint `json:"periods,omitempty" struct:"periods,omitempty"`
Usage cgcommon.CPUUsage `json:"usage" struct:"usage"`
User cgcommon.CPUUsage `json:"user" struct:"user"`
System cgcommon.CPUUsage `json:"system" struct:"system"`
}
// ThrottledField contains the `throttled` information for the CPU stats
type ThrottledField struct {
Us opt.Uint `json:"us,omitempty" struct:"us,omitempty"`
Periods opt.Uint `json:"periods,omitempty" struct:"periods,omitempty"`
}
// IsZero implements the IsZero interface for ThrottledField
func (t ThrottledField) IsZero() bool {
return t.Us.IsZero() && t.Periods.IsZero()
}
// Get fetches memory subsystem metrics for V2 cgroups
func (cpu *CPUSubsystem) Get(path string) error {
var err error
cpu.Pressure, err = cgcommon.GetPressure(filepath.Join(path, "cpu.pressure"))
// Not all systems have pressure stats. Treat this as a soft error.
if os.IsNotExist(err) {
return nil
}
if err != nil {
return fmt.Errorf("error fetching Pressure data: %w", err)
}
cpu.Stats, err = getStats(path)
if err != nil {
return fmt.Errorf("error fetching CPU stat data: %w", err)
}
return nil
}
// getStats returns the cpu.stats data
func getStats(path string) (CPUStats, error) {
f, err := os.Open(filepath.Join(path, "cpu.stat"))
if err != nil {
if os.IsNotExist(err) {
return CPUStats{}, nil
}
return CPUStats{}, fmt.Errorf("error reading cpu.stat: %w", err)
}
defer f.Close()
sc := bufio.NewScanner(f)
data := CPUStats{}
for sc.Scan() {
key, val, err := cgcommon.ParseCgroupParamKeyValue(sc.Text())
if err != nil {
return data, fmt.Errorf("error parsing cpu.stat file: %w", err)
}
switch key {
case "usage_usec":
data.Usage.NS = val
case "user_usec":
data.User.NS = val
case "system_usec":
data.System.NS = val
case "nr_periods":
data.Periods = opt.UintWith(val)
case "nr_throttled":
data.Throttled.Periods = opt.UintWith(val)
case "throttled_usec":
data.Throttled.Us = opt.UintWith(val)
}
}
return data, nil
}