From 1e6da111557c20d285087933e870833064f92984 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Fri, 2 Sep 2022 14:05:37 +0100 Subject: pkg/rctl: Add a simple FreeBSD RCTL library Initially just supporting just rctl_get_racct for (*Container).GetContainerStats. [NO NEW TESTS NEEDED] we are not running any FreeBSD tests in CI Signed-off-by: Doug Rabson --- pkg/rctl/rctl.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 pkg/rctl/rctl.go diff --git a/pkg/rctl/rctl.go b/pkg/rctl/rctl.go new file mode 100644 index 000000000..135cc60cb --- /dev/null +++ b/pkg/rctl/rctl.go @@ -0,0 +1,47 @@ +//go:build freebsd +// +build freebsd + +package rctl + +// #include +import "C" + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "syscall" + "unsafe" + + "github.com/sirupsen/logrus" +) + +func GetRacct(filter string) (map[string]uint64, error) { + bp, err := syscall.ByteSliceFromString(filter) + if err != nil { + return nil, err + } + var buf [1024]byte + _, _, errno := syscall.Syscall6(syscall.SYS_RCTL_GET_RACCT, + uintptr(unsafe.Pointer(&bp[0])), + uintptr(len(bp)), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(len(buf)), 0, 0) + if errno != 0 { + return nil, fmt.Errorf("error calling rctl_get_racct with filter %s: %v", errno) + } + len := bytes.IndexByte(buf[:], byte(0)) + entries := strings.Split(string(buf[:len]), ",") + res := make(map[string]uint64) + for _, entry := range entries { + kv := strings.SplitN(entry, "=", 2) + key := kv[0] + val, err := strconv.ParseUint(kv[1], 10, 0) + if err != nil { + logrus.Warnf("unexpected rctl entry, ignoring: %s", entry) + } + res[key] = val + } + return res, nil +} -- cgit v1.2.3-54-g00ecf From 47bd9e8110b4e2de04fffa27a16c84e24921cd69 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Fri, 2 Sep 2022 14:07:17 +0100 Subject: libpod: Move stats.go to stats_linux.go for consistency [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/stats.go | 161 -------------------------------------------------- libpod/stats_linux.go | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 161 deletions(-) delete mode 100644 libpod/stats.go create mode 100644 libpod/stats_linux.go diff --git a/libpod/stats.go b/libpod/stats.go deleted file mode 100644 index c7e9e5128..000000000 --- a/libpod/stats.go +++ /dev/null @@ -1,161 +0,0 @@ -//go:build linux -// +build linux - -package libpod - -import ( - "fmt" - "math" - "strings" - "syscall" - "time" - - runccgroup "github.com/opencontainers/runc/libcontainer/cgroups" - - "github.com/containers/common/pkg/cgroups" - "github.com/containers/podman/v4/libpod/define" -) - -// GetContainerStats gets the running stats for a given container. -// The previousStats is used to correctly calculate cpu percentages. You -// should pass nil if there is no previous stat for this container. -func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) { - stats := new(define.ContainerStats) - stats.ContainerID = c.ID() - stats.Name = c.Name() - - if c.config.NoCgroups { - return nil, fmt.Errorf("cannot run top on container %s as it did not create a cgroup: %w", c.ID(), define.ErrNoCgroups) - } - - if !c.batched { - c.lock.Lock() - defer c.lock.Unlock() - if err := c.syncContainer(); err != nil { - return stats, err - } - } - - // returns stats with the fields' default values respective of their type - if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused { - return stats, nil - } - - if previousStats == nil { - previousStats = &define.ContainerStats{ - // if we have no prev stats use the container start time as prev time - // otherwise we cannot correctly calculate the CPU percentage - SystemNano: uint64(c.state.StartedTime.UnixNano()), - } - } - - cgroupPath, err := c.cGroupPath() - if err != nil { - return nil, err - } - cgroup, err := cgroups.Load(cgroupPath) - if err != nil { - return stats, fmt.Errorf("unable to load cgroup at %s: %w", cgroupPath, err) - } - - // Ubuntu does not have swap memory in cgroups because swap is often not enabled. - cgroupStats, err := cgroup.Stat() - if err != nil { - return stats, fmt.Errorf("unable to obtain cgroup stats: %w", err) - } - conState := c.state.State - netStats, err := getContainerNetIO(c) - if err != nil { - return nil, err - } - - // If the current total usage in the cgroup is less than what was previously - // recorded then it means the container was restarted and runs in a new cgroup - if previousStats.Duration > cgroupStats.CpuStats.CpuUsage.TotalUsage { - previousStats = &define.ContainerStats{} - } - - previousCPU := previousStats.CPUNano - now := uint64(time.Now().UnixNano()) - stats.Duration = cgroupStats.CpuStats.CpuUsage.TotalUsage - stats.UpTime = time.Duration(stats.Duration) - stats.CPU = calculateCPUPercent(cgroupStats, previousCPU, now, previousStats.SystemNano) - // calc the average cpu usage for the time the container is running - stats.AvgCPU = calculateCPUPercent(cgroupStats, 0, now, uint64(c.state.StartedTime.UnixNano())) - stats.MemUsage = cgroupStats.MemoryStats.Usage.Usage - stats.MemLimit = c.getMemLimit() - stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100 - stats.PIDs = 0 - if conState == define.ContainerStateRunning || conState == define.ContainerStatePaused { - stats.PIDs = cgroupStats.PidsStats.Current - } - stats.BlockInput, stats.BlockOutput = calculateBlockIO(cgroupStats) - stats.CPUNano = cgroupStats.CpuStats.CpuUsage.TotalUsage - stats.CPUSystemNano = cgroupStats.CpuStats.CpuUsage.UsageInKernelmode - stats.SystemNano = now - stats.PerCPU = cgroupStats.CpuStats.CpuUsage.PercpuUsage - // Handle case where the container is not in a network namespace - if netStats != nil { - stats.NetInput = netStats.TxBytes - stats.NetOutput = netStats.RxBytes - } else { - stats.NetInput = 0 - stats.NetOutput = 0 - } - - return stats, nil -} - -// getMemory limit returns the memory limit for a container -func (c *Container) getMemLimit() uint64 { - memLimit := uint64(math.MaxUint64) - - if c.config.Spec.Linux != nil && c.config.Spec.Linux.Resources != nil && - c.config.Spec.Linux.Resources.Memory != nil && c.config.Spec.Linux.Resources.Memory.Limit != nil { - memLimit = uint64(*c.config.Spec.Linux.Resources.Memory.Limit) - } - - si := &syscall.Sysinfo_t{} - err := syscall.Sysinfo(si) - if err != nil { - return memLimit - } - - //nolint:unconvert - physicalLimit := uint64(si.Totalram) - - if memLimit <= 0 || memLimit > physicalLimit { - return physicalLimit - } - - return memLimit -} - -// calculateCPUPercent calculates the cpu usage using the latest measurement in stats. -// previousCPU is the last value of stats.CPU.Usage.Total measured at the time previousSystem. -// (now - previousSystem) is the time delta in nanoseconds, between the measurement in previousCPU -// and the updated value in stats. -func calculateCPUPercent(stats *runccgroup.Stats, previousCPU, now, previousSystem uint64) float64 { - var ( - cpuPercent = 0.0 - cpuDelta = float64(stats.CpuStats.CpuUsage.TotalUsage - previousCPU) - systemDelta = float64(now - previousSystem) - ) - if systemDelta > 0.0 && cpuDelta > 0.0 { - // gets a ratio of container cpu usage total, and multiplies that by 100 to get a percentage - cpuPercent = (cpuDelta / systemDelta) * 100 - } - return cpuPercent -} - -func calculateBlockIO(stats *runccgroup.Stats) (read uint64, write uint64) { - for _, blkIOEntry := range stats.BlkioStats.IoServiceBytesRecursive { - switch strings.ToLower(blkIOEntry.Op) { - case "read": - read += blkIOEntry.Value - case "write": - write += blkIOEntry.Value - } - } - return -} diff --git a/libpod/stats_linux.go b/libpod/stats_linux.go new file mode 100644 index 000000000..c7e9e5128 --- /dev/null +++ b/libpod/stats_linux.go @@ -0,0 +1,161 @@ +//go:build linux +// +build linux + +package libpod + +import ( + "fmt" + "math" + "strings" + "syscall" + "time" + + runccgroup "github.com/opencontainers/runc/libcontainer/cgroups" + + "github.com/containers/common/pkg/cgroups" + "github.com/containers/podman/v4/libpod/define" +) + +// GetContainerStats gets the running stats for a given container. +// The previousStats is used to correctly calculate cpu percentages. You +// should pass nil if there is no previous stat for this container. +func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) { + stats := new(define.ContainerStats) + stats.ContainerID = c.ID() + stats.Name = c.Name() + + if c.config.NoCgroups { + return nil, fmt.Errorf("cannot run top on container %s as it did not create a cgroup: %w", c.ID(), define.ErrNoCgroups) + } + + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + if err := c.syncContainer(); err != nil { + return stats, err + } + } + + // returns stats with the fields' default values respective of their type + if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused { + return stats, nil + } + + if previousStats == nil { + previousStats = &define.ContainerStats{ + // if we have no prev stats use the container start time as prev time + // otherwise we cannot correctly calculate the CPU percentage + SystemNano: uint64(c.state.StartedTime.UnixNano()), + } + } + + cgroupPath, err := c.cGroupPath() + if err != nil { + return nil, err + } + cgroup, err := cgroups.Load(cgroupPath) + if err != nil { + return stats, fmt.Errorf("unable to load cgroup at %s: %w", cgroupPath, err) + } + + // Ubuntu does not have swap memory in cgroups because swap is often not enabled. + cgroupStats, err := cgroup.Stat() + if err != nil { + return stats, fmt.Errorf("unable to obtain cgroup stats: %w", err) + } + conState := c.state.State + netStats, err := getContainerNetIO(c) + if err != nil { + return nil, err + } + + // If the current total usage in the cgroup is less than what was previously + // recorded then it means the container was restarted and runs in a new cgroup + if previousStats.Duration > cgroupStats.CpuStats.CpuUsage.TotalUsage { + previousStats = &define.ContainerStats{} + } + + previousCPU := previousStats.CPUNano + now := uint64(time.Now().UnixNano()) + stats.Duration = cgroupStats.CpuStats.CpuUsage.TotalUsage + stats.UpTime = time.Duration(stats.Duration) + stats.CPU = calculateCPUPercent(cgroupStats, previousCPU, now, previousStats.SystemNano) + // calc the average cpu usage for the time the container is running + stats.AvgCPU = calculateCPUPercent(cgroupStats, 0, now, uint64(c.state.StartedTime.UnixNano())) + stats.MemUsage = cgroupStats.MemoryStats.Usage.Usage + stats.MemLimit = c.getMemLimit() + stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100 + stats.PIDs = 0 + if conState == define.ContainerStateRunning || conState == define.ContainerStatePaused { + stats.PIDs = cgroupStats.PidsStats.Current + } + stats.BlockInput, stats.BlockOutput = calculateBlockIO(cgroupStats) + stats.CPUNano = cgroupStats.CpuStats.CpuUsage.TotalUsage + stats.CPUSystemNano = cgroupStats.CpuStats.CpuUsage.UsageInKernelmode + stats.SystemNano = now + stats.PerCPU = cgroupStats.CpuStats.CpuUsage.PercpuUsage + // Handle case where the container is not in a network namespace + if netStats != nil { + stats.NetInput = netStats.TxBytes + stats.NetOutput = netStats.RxBytes + } else { + stats.NetInput = 0 + stats.NetOutput = 0 + } + + return stats, nil +} + +// getMemory limit returns the memory limit for a container +func (c *Container) getMemLimit() uint64 { + memLimit := uint64(math.MaxUint64) + + if c.config.Spec.Linux != nil && c.config.Spec.Linux.Resources != nil && + c.config.Spec.Linux.Resources.Memory != nil && c.config.Spec.Linux.Resources.Memory.Limit != nil { + memLimit = uint64(*c.config.Spec.Linux.Resources.Memory.Limit) + } + + si := &syscall.Sysinfo_t{} + err := syscall.Sysinfo(si) + if err != nil { + return memLimit + } + + //nolint:unconvert + physicalLimit := uint64(si.Totalram) + + if memLimit <= 0 || memLimit > physicalLimit { + return physicalLimit + } + + return memLimit +} + +// calculateCPUPercent calculates the cpu usage using the latest measurement in stats. +// previousCPU is the last value of stats.CPU.Usage.Total measured at the time previousSystem. +// (now - previousSystem) is the time delta in nanoseconds, between the measurement in previousCPU +// and the updated value in stats. +func calculateCPUPercent(stats *runccgroup.Stats, previousCPU, now, previousSystem uint64) float64 { + var ( + cpuPercent = 0.0 + cpuDelta = float64(stats.CpuStats.CpuUsage.TotalUsage - previousCPU) + systemDelta = float64(now - previousSystem) + ) + if systemDelta > 0.0 && cpuDelta > 0.0 { + // gets a ratio of container cpu usage total, and multiplies that by 100 to get a percentage + cpuPercent = (cpuDelta / systemDelta) * 100 + } + return cpuPercent +} + +func calculateBlockIO(stats *runccgroup.Stats) (read uint64, write uint64) { + for _, blkIOEntry := range stats.BlkioStats.IoServiceBytesRecursive { + switch strings.ToLower(blkIOEntry.Op) { + case "read": + read += blkIOEntry.Value + case "write": + write += blkIOEntry.Value + } + } + return +} -- cgit v1.2.3-54-g00ecf From b3e978e43b3e01a68e5b8890fe79be64eb976e65 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Tue, 13 Sep 2022 16:05:54 +0100 Subject: libpod: Split out the common code from GetContainerStats This moves the cgroups code to a new method getPlatformContainerStats. [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/stats_common.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ libpod/stats_linux.go | 50 ++++++++++++++------------------------------------ 2 files changed, 63 insertions(+), 36 deletions(-) create mode 100644 libpod/stats_common.go diff --git a/libpod/stats_common.go b/libpod/stats_common.go new file mode 100644 index 000000000..122160bda --- /dev/null +++ b/libpod/stats_common.go @@ -0,0 +1,49 @@ +//go:build linux || freebsd +// +build linux freebsd + +package libpod + +import ( + "fmt" + + "github.com/containers/podman/v4/libpod/define" +) + +// GetContainerStats gets the running stats for a given container. +// The previousStats is used to correctly calculate cpu percentages. You +// should pass nil if there is no previous stat for this container. +func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) { + stats := new(define.ContainerStats) + stats.ContainerID = c.ID() + stats.Name = c.Name() + + if c.config.NoCgroups { + return nil, fmt.Errorf("cannot run top on container %s as it did not create a cgroup: %w", c.ID(), define.ErrNoCgroups) + } + + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + if err := c.syncContainer(); err != nil { + return stats, err + } + } + + // returns stats with the fields' default values respective of their type + if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused { + return stats, nil + } + + if previousStats == nil { + previousStats = &define.ContainerStats{ + // if we have no prev stats use the container start time as prev time + // otherwise we cannot correctly calculate the CPU percentage + SystemNano: uint64(c.state.StartedTime.UnixNano()), + } + } + + if err := c.getPlatformContainerStats(stats, previousStats); err != nil { + return nil, err + } + return stats, nil +} diff --git a/libpod/stats_linux.go b/libpod/stats_linux.go index c7e9e5128..ad8f33c91 100644 --- a/libpod/stats_linux.go +++ b/libpod/stats_linux.go @@ -16,57 +16,33 @@ import ( "github.com/containers/podman/v4/libpod/define" ) -// GetContainerStats gets the running stats for a given container. -// The previousStats is used to correctly calculate cpu percentages. You -// should pass nil if there is no previous stat for this container. -func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) { - stats := new(define.ContainerStats) - stats.ContainerID = c.ID() - stats.Name = c.Name() - +// getPlatformContainerStats gets the platform-specific running stats +// for a given container. The previousStats is used to correctly +// calculate cpu percentages. You should pass nil if there is no +// previous stat for this container. +func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, previousStats *define.ContainerStats) error { if c.config.NoCgroups { - return nil, fmt.Errorf("cannot run top on container %s as it did not create a cgroup: %w", c.ID(), define.ErrNoCgroups) - } - - if !c.batched { - c.lock.Lock() - defer c.lock.Unlock() - if err := c.syncContainer(); err != nil { - return stats, err - } - } - - // returns stats with the fields' default values respective of their type - if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused { - return stats, nil - } - - if previousStats == nil { - previousStats = &define.ContainerStats{ - // if we have no prev stats use the container start time as prev time - // otherwise we cannot correctly calculate the CPU percentage - SystemNano: uint64(c.state.StartedTime.UnixNano()), - } + return fmt.Errorf("cannot run top on container %s as it did not create a cgroup: %w", c.ID(), define.ErrNoCgroups) } cgroupPath, err := c.cGroupPath() if err != nil { - return nil, err + return err } cgroup, err := cgroups.Load(cgroupPath) if err != nil { - return stats, fmt.Errorf("unable to load cgroup at %s: %w", cgroupPath, err) + return fmt.Errorf("unable to load cgroup at %s: %w", cgroupPath, err) } // Ubuntu does not have swap memory in cgroups because swap is often not enabled. cgroupStats, err := cgroup.Stat() if err != nil { - return stats, fmt.Errorf("unable to obtain cgroup stats: %w", err) + return fmt.Errorf("unable to obtain cgroup stats: %w", err) } conState := c.state.State netStats, err := getContainerNetIO(c) if err != nil { - return nil, err + return err } // If the current total usage in the cgroup is less than what was previously @@ -103,7 +79,7 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de stats.NetOutput = 0 } - return stats, nil + return nil } // getMemory limit returns the memory limit for a container @@ -133,7 +109,9 @@ func (c *Container) getMemLimit() uint64 { // calculateCPUPercent calculates the cpu usage using the latest measurement in stats. // previousCPU is the last value of stats.CPU.Usage.Total measured at the time previousSystem. -// (now - previousSystem) is the time delta in nanoseconds, between the measurement in previousCPU +// +// (now - previousSystem) is the time delta in nanoseconds, between the measurement in previousCPU +// // and the updated value in stats. func calculateCPUPercent(stats *runccgroup.Stats, previousCPU, now, previousSystem uint64) float64 { var ( -- cgit v1.2.3-54-g00ecf From b47ce9a6e0e9fa8e137c8338a08dffa256081b48 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Fri, 2 Sep 2022 14:22:02 +0100 Subject: libpod: Add support for 'podman stats' on FreeBSD [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/stats_freebsd.go | 153 ++++++++++++++++++++++++++++++++++++++++++++ libpod/stats_unsupported.go | 4 +- 2 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 libpod/stats_freebsd.go diff --git a/libpod/stats_freebsd.go b/libpod/stats_freebsd.go new file mode 100644 index 000000000..53bc3f19a --- /dev/null +++ b/libpod/stats_freebsd.go @@ -0,0 +1,153 @@ +package libpod + +import ( + "fmt" + "math" + "strings" + "time" + + "github.com/containers/common/pkg/cgroups" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/rctl" + "github.com/containers/storage/pkg/system" + "github.com/sirupsen/logrus" +) + +// getPlatformContainerStats gets the platform-specific running stats +// for a given container. The previousStats is used to correctly +// calculate cpu percentages. You should pass nil if there is no +// previous stat for this container. +func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, previousStats *define.ContainerStats) error { + now := uint64(time.Now().UnixNano()) + + jailName := c.ID() + if c.state.NetNS != nil { + jailName = c.state.NetNS.Name + "." + jailName + } + entries, err := rctl.GetRacct("jail:" + jailName) + if err != nil { + return fmt.Errorf("unable to read accounting for %s: %w", jailName, err) + } + + // If the current total usage is less than what was previously + // recorded then it means the container was restarted and runs + // in a new jail + if dur, ok := entries["wallclock"]; ok { + if previousStats.Duration > dur*1000000000 { + previousStats = &define.ContainerStats{} + } + } + + for key, val := range entries { + switch key { + case "cputime": // CPU time, in seconds + stats.CPUNano = val * 1000000000 + stats.AvgCPU = calculateCPUPercent(stats.CPUNano, 0, now, uint64(c.state.StartedTime.UnixNano())) + case "datasize": // data size, in bytes + case "stacksize": // stack size, in bytes + case "coredumpsize": // core dump size, in bytes + case "memoryuse": // resident set size, in bytes + stats.MemUsage = val + case "memorylocked": // locked memory, in bytes + case "maxproc": // number of processes + stats.PIDs = val + case "openfiles": // file descriptor table size + case "vmemoryuse": // address space limit, in bytes + case "pseudoterminals": // number of PTYs + case "swapuse": // swap space that may be reserved or used, in bytes + case "nthr": // number of threads + case "msgqqueued": // number of queued SysV messages + case "msgqsize": // SysV message queue size, in bytes + case "nmsgq": // number of SysV message queues + case "nsem": // number of SysV semaphores + case "nsemop": // number of SysV semaphores modified in a single semop(2) call + case "nshm": // number of SysV shared memory segments + case "shmsize": // SysV shared memory size, in bytes + case "wallclock": // wallclock time, in seconds + stats.Duration = val * 1000000000 + stats.UpTime = time.Duration(stats.Duration) + case "pcpu": // %CPU, in percents of a single CPU core + stats.CPU = float64(val) + case "readbps": // filesystem reads, in bytes per second + stats.BlockInput = val + case "writebps": // filesystem writes, in bytes per second + stats.BlockOutput = val + case "readiops": // filesystem reads, in operations per second + case "writeiops": // filesystem writes, in operations per second + } + } + stats.MemLimit = c.getMemLimit() + stats.SystemNano = now + + netStats, err := getContainerNetIO(c) + if err != nil { + return err + } + + // Handle case where the container is not in a network namespace + if netStats != nil { + stats.NetInput = netStats.TxBytes + stats.NetOutput = netStats.RxBytes + } else { + stats.NetInput = 0 + stats.NetOutput = 0 + } + + return nil +} + +// getMemory limit returns the memory limit for a container +func (c *Container) getMemLimit() uint64 { + memLimit := uint64(math.MaxUint64) + + if c.config.Spec.Linux != nil && c.config.Spec.Linux.Resources != nil && + c.config.Spec.Linux.Resources.Memory != nil && c.config.Spec.Linux.Resources.Memory.Limit != nil { + memLimit = uint64(*c.config.Spec.Linux.Resources.Memory.Limit) + } + + mi, err := system.ReadMemInfo() + if err != nil { + logrus.Errorf("ReadMemInfo error: %v", err) + return 0 + } + + //nolint:unconvert + physicalLimit := uint64(mi.MemTotal) + + if memLimit <= 0 || memLimit > physicalLimit { + return physicalLimit + } + + return memLimit +} + +// calculateCPUPercent calculates the cpu usage using the latest measurement in stats. +// previousCPU is the last value of stats.CPU.Usage.Total measured at the time previousSystem. +// +// (now - previousSystem) is the time delta in nanoseconds, between the measurement in previousCPU +// +// and the updated value in stats. +func calculateCPUPercent(currentCPU, previousCPU, now, previousSystem uint64) float64 { + var ( + cpuPercent = 0.0 + cpuDelta = float64(currentCPU - previousCPU) + systemDelta = float64(now - previousSystem) + ) + if systemDelta > 0.0 && cpuDelta > 0.0 { + // gets a ratio of container cpu usage total, and multiplies that by 100 to get a percentage + cpuPercent = (cpuDelta / systemDelta) * 100 + } + return cpuPercent +} + +func calculateBlockIO(stats *cgroups.Metrics) (read uint64, write uint64) { + for _, blkIOEntry := range stats.Blkio.IoServiceBytesRecursive { + switch strings.ToLower(blkIOEntry.Op) { + case "read": + read += blkIOEntry.Value + case "write": + write += blkIOEntry.Value + } + } + return +} diff --git a/libpod/stats_unsupported.go b/libpod/stats_unsupported.go index b23333c2e..3094e2eaa 100644 --- a/libpod/stats_unsupported.go +++ b/libpod/stats_unsupported.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !freebsd +// +build !linux,!freebsd package libpod -- cgit v1.2.3-54-g00ecf