|
|
@@ -1,42 +1,84 @@
|
|
|
package cgroup
|
|
|
|
|
|
import (
|
|
|
+ "fmt"
|
|
|
+ "io/ioutil"
|
|
|
"path"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
)
|
|
|
|
|
|
-type ThrottlingStat struct {
|
|
|
- Periods uint64
|
|
|
- ThrottledPeriods uint64
|
|
|
+type CPUStat struct {
|
|
|
+ UsageSeconds float64
|
|
|
ThrottledTimeSeconds float64
|
|
|
+ LimitCores float64
|
|
|
}
|
|
|
|
|
|
-func (cg Cgroup) ThrottledTimeSeconds() (float64, error) {
|
|
|
- vars, err := readVariablesFromFile(path.Join(cgRoot, "cpu", cg.subsystems["cpu"], "cpu.stat"))
|
|
|
- if err != nil {
|
|
|
- return 0, err
|
|
|
+func (cg Cgroup) CpuStat() (*CPUStat, error) {
|
|
|
+ if cg.Version == V1 {
|
|
|
+ return cg.cpuStatV1()
|
|
|
}
|
|
|
- return float64(vars["throttled_time"]) / 1e9, nil
|
|
|
+ return cg.cpuStatV2()
|
|
|
}
|
|
|
|
|
|
-func (cg Cgroup) CpuUsageSeconds() (float64, error) {
|
|
|
+func (cg Cgroup) cpuStatV1() (*CPUStat, error) {
|
|
|
+ throttling, err := readVariablesFromFile(path.Join(cgRoot, "cpu", cg.subsystems["cpu"], "cpu.stat"))
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
usageNs, err := readIntFromFile(path.Join(cgRoot, "cpuacct", cg.subsystems["cpuacct"], "cpuacct.usage"))
|
|
|
if err != nil {
|
|
|
- return 0, err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- return float64(usageNs) / 1e9, err
|
|
|
-}
|
|
|
-
|
|
|
-func (cg Cgroup) CpuQuotaCores() (float64, error) {
|
|
|
periodUs, err := readIntFromFile(path.Join(cgRoot, "cpu", cg.subsystems["cpu"], "cpu.cfs_period_us"))
|
|
|
if err != nil {
|
|
|
- return -1, err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
quotaUs, err := readIntFromFile(path.Join(cgRoot, "cpu", cg.subsystems["cpu"], "cpu.cfs_quota_us"))
|
|
|
if err != nil {
|
|
|
- return -1, err
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ res := &CPUStat{
|
|
|
+ UsageSeconds: float64(usageNs) / 1e9,
|
|
|
+ ThrottledTimeSeconds: float64(throttling["throttled_time"]) / 1e9,
|
|
|
+ }
|
|
|
+ if quotaUs > 0 {
|
|
|
+ res.LimitCores = float64(quotaUs) / float64(periodUs)
|
|
|
+ }
|
|
|
+ return res, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (cg Cgroup) cpuStatV2() (*CPUStat, error) {
|
|
|
+ vars, err := readVariablesFromFile(path.Join(cgRoot, cg.subsystems[""], "cpu.stat"))
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ res := &CPUStat{
|
|
|
+ UsageSeconds: float64(vars["usage_usec"]) / 1e6,
|
|
|
+ ThrottledTimeSeconds: float64(vars["throttled_usec"]) / 1e6,
|
|
|
+ }
|
|
|
+ payload, err := ioutil.ReadFile(path.Join(cgRoot, cg.subsystems[""], "cpu.max"))
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ data := strings.TrimSpace(string(payload))
|
|
|
+ parts := strings.Fields(data)
|
|
|
+ if len(parts) != 2 {
|
|
|
+ return nil, fmt.Errorf("invalid cpu.max payload: %s", data)
|
|
|
+ }
|
|
|
+ if parts[0] == "max" { //no limit
|
|
|
+ return res, nil
|
|
|
+ }
|
|
|
+ quotaUs, err := strconv.ParseUint(parts[0], 10, 64)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("invalid quota value in cpu.max: %s", parts[0])
|
|
|
+ }
|
|
|
+ periodUs, err := strconv.ParseUint(parts[1], 10, 64)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("invalid period value in cpu.max: %s", parts[1])
|
|
|
}
|
|
|
- if quotaUs < 0 {
|
|
|
- return -1, nil
|
|
|
+ if periodUs > 0 {
|
|
|
+ res.LimitCores = float64(quotaUs) / float64(periodUs)
|
|
|
}
|
|
|
- return float64(quotaUs) / float64(periodUs), nil
|
|
|
+ return res, nil
|
|
|
}
|