diff options
-rw-r--r-- | libpod/stats.go | 2 | ||||
-rw-r--r-- | libpod/stats_config.go | 28 | ||||
-rw-r--r-- | pkg/api/handlers/generic/containers_stats.go | 15 | ||||
-rw-r--r-- | pkg/cgroups/cgroups.go | 15 | ||||
-rw-r--r-- | pkg/cgroups/cpu.go | 39 |
5 files changed, 75 insertions, 24 deletions
diff --git a/libpod/stats.go b/libpod/stats.go index 3b5e0958c..6f42afd18 100644 --- a/libpod/stats.go +++ b/libpod/stats.go @@ -66,7 +66,9 @@ func (c *Container) GetContainerStats(previousStats *ContainerStats) (*Container } stats.BlockInput, stats.BlockOutput = calculateBlockIO(cgroupStats) stats.CPUNano = cgroupStats.CPU.Usage.Total + stats.CPUSystemNano = cgroupStats.CPU.Usage.Kernel stats.SystemNano = now + stats.PerCPU = cgroupStats.CPU.Usage.PerCPU // Handle case where the container is not in a network namespace if netStats != nil { stats.NetInput = netStats.TxBytes diff --git a/libpod/stats_config.go b/libpod/stats_config.go index 9c7d97298..91d3d1493 100644 --- a/libpod/stats_config.go +++ b/libpod/stats_config.go @@ -2,17 +2,19 @@ package libpod // ContainerStats contains the statistics information for a running container type ContainerStats struct { - ContainerID string - Name string - CPU float64 - CPUNano uint64 - SystemNano uint64 - MemUsage uint64 - MemLimit uint64 - MemPerc float64 - NetInput uint64 - NetOutput uint64 - BlockInput uint64 - BlockOutput uint64 - PIDs uint64 + ContainerID string + Name string + PerCPU []uint64 + CPU float64 + CPUNano uint64 + CPUSystemNano uint64 + SystemNano uint64 + MemUsage uint64 + MemLimit uint64 + MemPerc float64 + NetInput uint64 + NetOutput uint64 + BlockInput uint64 + BlockOutput uint64 + PIDs uint64 } diff --git a/pkg/api/handlers/generic/containers_stats.go b/pkg/api/handlers/generic/containers_stats.go index cbc1be2f0..19e2cc882 100644 --- a/pkg/api/handlers/generic/containers_stats.go +++ b/pkg/api/handlers/generic/containers_stats.go @@ -61,14 +61,15 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { var preCPUStats docker.CPUStats if query.Stream { preRead = time.Now() + systemUsage, _ := cgroups.GetSystemCPUUsage() preCPUStats = docker.CPUStats{ CPUUsage: docker.CPUUsage{ TotalUsage: stats.CPUNano, - PercpuUsage: []uint64{uint64(stats.CPU)}, - UsageInKernelmode: 0, - UsageInUsermode: 0, + PercpuUsage: stats.PerCPU, + UsageInKernelmode: stats.CPUSystemNano, + UsageInUsermode: stats.CPUNano - stats.CPUSystemNano, }, - SystemUsage: 0, + SystemUsage: systemUsage, OnlineCPUs: 0, ThrottlingData: docker.ThrottlingData{}, } @@ -122,6 +123,8 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { InstanceID: "", } + systemUsage, _ := cgroups.GetSystemCPUUsage() + s := handlers.Stats{StatsJSON: docker.StatsJSON{ Stats: docker.Stats{ Read: time.Now(), @@ -143,11 +146,11 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { CPUStats: docker.CPUStats{ CPUUsage: docker.CPUUsage{ TotalUsage: cgroupStat.CPU.Usage.Total, - PercpuUsage: []uint64{uint64(stats.CPU)}, + PercpuUsage: cgroupStat.CPU.Usage.PerCPU, UsageInKernelmode: cgroupStat.CPU.Usage.Kernel, UsageInUsermode: cgroupStat.CPU.Usage.Total - cgroupStat.CPU.Usage.Kernel, }, - SystemUsage: 0, + SystemUsage: systemUsage, OnlineCPUs: uint32(len(cgroupStat.CPU.Usage.PerCPU)), ThrottlingData: docker.ThrottlingData{ Periods: 0, diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 96786223d..6c5b7978c 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -536,15 +536,14 @@ func (c *CgroupControl) Stat() (*Metrics, error) { return &m, nil } -func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, error) { +func readCgroup2MapPath(path string) (map[string][]string, error) { ret := map[string][]string{} - p := filepath.Join(cgroupRoot, ctr.path, name) - f, err := os.Open(p) + f, err := os.Open(path) if err != nil { if os.IsNotExist(err) { return ret, nil } - return nil, errors.Wrapf(err, "open file %s", p) + return nil, errors.Wrapf(err, "open file %s", path) } defer f.Close() scanner := bufio.NewScanner(f) @@ -557,7 +556,13 @@ func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, e ret[parts[0]] = parts[1:] } if err := scanner.Err(); err != nil { - return nil, errors.Wrapf(err, "parsing file %s", p) + return nil, errors.Wrapf(err, "parsing file %s", path) } return ret, nil } + +func readCgroup2MapFile(ctr *CgroupControl, name string) (map[string][]string, error) { + p := filepath.Join(cgroupRoot, ctr.path, name) + + return readCgroup2MapPath(p) +} diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go index a43a76b22..5f0a18031 100644 --- a/pkg/cgroups/cpu.go +++ b/pkg/cgroups/cpu.go @@ -121,3 +121,42 @@ func (c *cpuHandler) Stat(ctr *CgroupControl, m *Metrics) error { m.CPU = CPUMetrics{Usage: usage} return nil } + +// GetSystemCPUUsage returns the system usage for all the cgroups +func GetSystemCPUUsage() (uint64, error) { + cgroupv2, err := IsCgroup2UnifiedMode() + if err != nil { + return 0, err + } + if !cgroupv2 { + p := filepath.Join(cgroupRoot, CPUAcct, "cpuacct.usage") + return readFileAsUint64(p) + } + + files, err := ioutil.ReadDir(cgroupRoot) + if err != nil { + return 0, errors.Wrapf(err, "read directory %q", cgroupRoot) + } + var total uint64 + for _, file := range files { + if !file.IsDir() { + continue + } + p := filepath.Join(cgroupRoot, file.Name(), "cpu.stat") + + values, err := readCgroup2MapPath(p) + if err != nil { + return 0, err + } + + if val, found := values["usage_usec"]; found { + v, err := strconv.ParseUint(cleanString(val[0]), 10, 0) + if err != nil { + return 0, err + } + total += v * 1000 + } + + } + return total, nil +} |