mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
a263afaf6c
'parent' function is confusing with parent cgroup, it's actually parent path, so rename it to parentPath. The name 'data' is too common to be identified, rename it to cgroupData which is exactly what it is. Signed-off-by: Qiang Huang <h.huangqiang@huawei.com>
122 lines
3.1 KiB
Go
122 lines
3.1 KiB
Go
// +build linux
|
|
|
|
package fs
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
"github.com/opencontainers/runc/libcontainer/system"
|
|
)
|
|
|
|
const (
|
|
cgroupCpuacctStat = "cpuacct.stat"
|
|
nanosecondsInSecond = 1000000000
|
|
)
|
|
|
|
var clockTicks = uint64(system.GetClockTicks())
|
|
|
|
type CpuacctGroup struct {
|
|
}
|
|
|
|
func (s *CpuacctGroup) Name() string {
|
|
return "cpuacct"
|
|
}
|
|
|
|
func (s *CpuacctGroup) Apply(d *cgroupData) error {
|
|
// we just want to join this group even though we don't set anything
|
|
if _, err := d.join("cpuacct"); err != nil && !cgroups.IsNotFound(err) {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *CpuacctGroup) Set(path string, cgroup *configs.Cgroup) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *CpuacctGroup) Remove(d *cgroupData) error {
|
|
return removePath(d.path("cpuacct"))
|
|
}
|
|
|
|
func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
|
|
userModeUsage, kernelModeUsage, err := getCpuUsageBreakdown(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
totalUsage, err := getCgroupParamUint(path, "cpuacct.usage")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
percpuUsage, err := getPercpuUsage(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
stats.CpuStats.CpuUsage.TotalUsage = totalUsage
|
|
stats.CpuStats.CpuUsage.PercpuUsage = percpuUsage
|
|
stats.CpuStats.CpuUsage.UsageInUsermode = userModeUsage
|
|
stats.CpuStats.CpuUsage.UsageInKernelmode = kernelModeUsage
|
|
return nil
|
|
}
|
|
|
|
// Returns user and kernel usage breakdown in nanoseconds.
|
|
func getCpuUsageBreakdown(path string) (uint64, uint64, error) {
|
|
userModeUsage := uint64(0)
|
|
kernelModeUsage := uint64(0)
|
|
const (
|
|
userField = "user"
|
|
systemField = "system"
|
|
)
|
|
|
|
// Expected format:
|
|
// user <usage in ticks>
|
|
// system <usage in ticks>
|
|
data, err := ioutil.ReadFile(filepath.Join(path, cgroupCpuacctStat))
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
fields := strings.Fields(string(data))
|
|
if len(fields) != 4 {
|
|
return 0, 0, fmt.Errorf("failure - %s is expected to have 4 fields", filepath.Join(path, cgroupCpuacctStat))
|
|
}
|
|
if fields[0] != userField {
|
|
return 0, 0, fmt.Errorf("unexpected field %q in %q, expected %q", fields[0], cgroupCpuacctStat, userField)
|
|
}
|
|
if fields[2] != systemField {
|
|
return 0, 0, fmt.Errorf("unexpected field %q in %q, expected %q", fields[2], cgroupCpuacctStat, systemField)
|
|
}
|
|
if userModeUsage, err = strconv.ParseUint(fields[1], 10, 64); err != nil {
|
|
return 0, 0, err
|
|
}
|
|
if kernelModeUsage, err = strconv.ParseUint(fields[3], 10, 64); err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
return (userModeUsage * nanosecondsInSecond) / clockTicks, (kernelModeUsage * nanosecondsInSecond) / clockTicks, nil
|
|
}
|
|
|
|
func getPercpuUsage(path string) ([]uint64, error) {
|
|
percpuUsage := []uint64{}
|
|
data, err := ioutil.ReadFile(filepath.Join(path, "cpuacct.usage_percpu"))
|
|
if err != nil {
|
|
return percpuUsage, err
|
|
}
|
|
for _, value := range strings.Fields(string(data)) {
|
|
value, err := strconv.ParseUint(value, 10, 64)
|
|
if err != nil {
|
|
return percpuUsage, fmt.Errorf("Unable to convert param value to uint64: %s", err)
|
|
}
|
|
percpuUsage = append(percpuUsage, value)
|
|
}
|
|
return percpuUsage, nil
|
|
}
|