// Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. //go:build linux package cgroups import ( "strconv" "time" "github.com/DataDog/datadog-agent/pkg/util/pointer" ) func (c *cgroupV1) GetCPUStats(stats *CPUStats) error { if stats == nil { return &InvalidInputError{Desc: "input stats cannot be nil"} } if !c.controllerMounted("cpuacct") { return &ControllerNotFoundError{Controller: "cpuacct"} } if !c.controllerMounted("cpu") { return &ControllerNotFoundError{Controller: "cpu"} } c.parseCPUAcctController(stats) c.parseCPUController(stats) // Do not raise error if `cpuset` is not present as it's not used to retrieve key features if c.controllerMounted("cpuset") { c.parseCPUSetController(stats) } return nil } func (c *cgroupV1) parseCPUController(stats *CPUStats) { if err := parseSingleUnsignedStat(c.fr, c.pathFor("cpu", "cpu.shares"), &stats.Shares); err != nil { reportError(err) } if err := parse2ColumnStats(c.fr, c.pathFor("cpu", "cpu.stat"), 0, 1, func(key, value string) error { intVal, err := strconv.ParseUint(value, 10, 64) if err != nil { reportError(newValueError(value, err)) // Dont't stop parsing on a single faulty value return nil } switch key { case "nr_throttled": stats.ThrottledPeriods = &intVal case "throttled_time": stats.ThrottledTime = &intVal case "nr_periods": stats.ElapsedPeriods = &intVal } return nil }); err != nil { reportError(err) } if err := parseSingleUnsignedStat(c.fr, c.pathFor("cpu", "cpu.cfs_period_us"), &stats.SchedulerPeriod); err == nil { if stats.SchedulerPeriod != nil { stats.SchedulerPeriod = pointer.Ptr(*stats.SchedulerPeriod * uint64(time.Microsecond)) } } else { reportError(err) } var tempValue *int64 if err := parseSingleSignedStat(c.fr, c.pathFor("cpu", "cpu.cfs_quota_us"), &tempValue); err == nil { if tempValue != nil && *tempValue != -1 { stats.SchedulerQuota = pointer.Ptr(uint64(*tempValue) * uint64(time.Microsecond)) } } else { reportError(err) } } func (c *cgroupV1) parseCPUAcctController(stats *CPUStats) { if err := parseSingleUnsignedStat(c.fr, c.pathFor("cpuacct", "cpuacct.usage"), &stats.Total); err != nil { reportError(err) } if err := parse2ColumnStats(c.fr, c.pathFor("cpuacct", "cpuacct.stat"), 0, 1, parseV1CPUAcctStatFn(stats)); err != nil { reportError(err) } } func (c *cgroupV1) parseCPUSetController(stats *CPUStats) { // Normally there's only one line, but as the parser works line by line anyway, we do support multiple lines var cpuCount uint64 err := parseFile(c.fr, c.pathFor("cpuset", "cpuset.cpus"), func(line string) error { cpuCount += ParseCPUSetFormat(line) return nil }) if err != nil { reportError(err) } else if cpuCount != 0 { stats.CPUCount = &cpuCount } } func parseV1CPUAcctStatFn(stats *CPUStats) func(key, val string) error { return func(key, val string) error { intVal, err := strconv.ParseUint(val, 10, 64) if err != nil { reportError(newValueError(val, err)) return nil } switch key { case "user": stats.User = pointer.Ptr(intVal * UserHZToNano) case "system": stats.System = pointer.Ptr(intVal * UserHZToNano) } return nil } }