metric/system/cgroup/cgv1/cpu.go (99 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 cgv1
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. This
// subsystem is used to guarantee a minimum number of cpu shares to the cgroup
// when the system is busy. This subsystem does not track CPU usage, for that
// information see the "cpuacct" subsystem.
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.
// Completely Fair Scheduler (CFS) settings.
CFS CFS `json:"cfs,omitempty"`
// Real-time (RT) Scheduler settings.
RT RT `json:"rt,omitempty"`
// CPU time statistics for tasks in this cgroup.
Stats CPUStats `json:"stats,omitempty"`
}
// RT contains the tunable parameters for the real-time scheduler.
type RT struct {
// Period of time in microseconds for how regularly the cgroup's access to
// CPU resources should be reallocated.
Period opt.Us `json:"period" struct:"period"`
// Period of time in microseconds for the longest continuous period in which
// the tasks in the cgroup have access to CPU resources.
Runtime opt.Us `json:"runtime" struct:"runtime"`
}
// CFS contains the tunable parameters for the completely fair scheduler.
type CFS struct {
// Period of time in microseconds for how regularly the cgroup's access to
// CPU resources should be reallocated.
PeriodMicros opt.Us `json:"period" struct:"period"`
// Total amount of time in microseconds for which all tasks in the cgroup
// can run during one period.
QuotaMicros opt.Us `json:"quota" struct:"quota"`
// Relative share of CPU time available to tasks the cgroup. The value is
// an integer greater than or equal to 2.
Shares uint64 `json:"shares"`
}
// CPUStats contains stats that indicate the extent to which this cgroup's
// CPU usage was throttled.
type CPUStats struct {
// Number of periods with throttling active.
Periods uint64 `json:"periods,omitempty"`
Throttled ThrottledField `json:"throttled" struct:"throttled"`
}
// ThrottledField contains the `throttled` information for the CPU stats
type ThrottledField struct {
Us uint64 `json:"us" struct:"us"`
Periods uint64 `json:"periods" struct:"periods"`
}
// Get reads metrics from the "cpu" subsystem. path is the filepath to the
// cgroup hierarchy to read.
func (cpu *CPUSubsystem) Get(path string) error {
if err := cpuCFS(path, cpu); err != nil {
return fmt.Errorf("error fetching CFS data: %w", err)
}
if err := cpuRT(path, cpu); err != nil {
return fmt.Errorf("error fetching RT data: %w", err)
}
if err := cpuStat(path, cpu); err != nil {
return fmt.Errorf("error fetching CPU stats: %w", err)
}
return nil
}
func cpuStat(path string, cpu *CPUSubsystem) error {
f, err := os.Open(filepath.Join(path, "cpu.stat"))
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
defer f.Close()
sc := bufio.NewScanner(f)
for sc.Scan() {
t, v, err := cgcommon.ParseCgroupParamKeyValue(sc.Text())
if err != nil {
return err
}
switch t {
case "nr_periods":
cpu.Stats.Periods = v
case "nr_throttled":
cpu.Stats.Throttled.Periods = v
case "throttled_time":
cpu.Stats.Throttled.Us = v
}
}
return sc.Err()
}
func cpuCFS(path string, cpu *CPUSubsystem) error {
var err error
cpu.CFS.PeriodMicros.Us, err = cgcommon.ParseUintFromFile(path, "cpu.cfs_period_us")
if err != nil {
return err
}
cpu.CFS.QuotaMicros.Us, err = cgcommon.ParseUintFromFile(path, "cpu.cfs_quota_us")
if err != nil {
return err
}
cpu.CFS.Shares, err = cgcommon.ParseUintFromFile(path, "cpu.shares")
if err != nil {
return err
}
return nil
}
func cpuRT(path string, cpu *CPUSubsystem) error {
var err error
cpu.RT.Period.Us, err = cgcommon.ParseUintFromFile(path, "cpu.rt_period_us")
if err != nil {
return err
}
cpu.RT.Runtime.Us, err = cgcommon.ParseUintFromFile(path, "cpu.rt_runtime_us")
if err != nil {
return err
}
return nil
}