mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-10 21:53:57 +08:00
5a6e1e18f9
Fix *some* of the prealloc linter warnings. While it does not make sense to address all warnings (or add prealloc to the list of linters we run in CI), some do make sense. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
218 lines
6.1 KiB
Go
218 lines
6.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/opencontainers/cgroups"
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
"github.com/opencontainers/runc/libcontainer/intelrdt"
|
|
"github.com/opencontainers/runc/types"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
var eventsCommand = cli.Command{
|
|
Name: "events",
|
|
Usage: "display container events such as OOM notifications, cpu, memory, and IO usage statistics",
|
|
ArgsUsage: `<container-id>
|
|
|
|
Where "<container-id>" is the name for the instance of the container.`,
|
|
Description: `The events command displays information about the container. By default the
|
|
information is displayed once every 5 seconds.`,
|
|
Flags: []cli.Flag{
|
|
cli.DurationFlag{Name: "interval", Value: 5 * time.Second, Usage: "set the stats collection interval"},
|
|
cli.BoolFlag{Name: "stats", Usage: "display the container's stats then exit"},
|
|
},
|
|
Action: func(context *cli.Context) error {
|
|
if err := checkArgs(context, 1, exactArgs); err != nil {
|
|
return err
|
|
}
|
|
container, err := getContainer(context)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
duration := context.Duration("interval")
|
|
if duration <= 0 {
|
|
return errors.New("duration interval must be greater than 0")
|
|
}
|
|
status, err := container.Status()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if status == libcontainer.Stopped {
|
|
return fmt.Errorf("container with id %s is not running", container.ID())
|
|
}
|
|
var (
|
|
stats = make(chan *libcontainer.Stats, 1)
|
|
events = make(chan *types.Event, 1024)
|
|
group = &sync.WaitGroup{}
|
|
)
|
|
group.Add(1)
|
|
go func() {
|
|
defer group.Done()
|
|
enc := json.NewEncoder(os.Stdout)
|
|
for e := range events {
|
|
if err := enc.Encode(e); err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
}
|
|
}()
|
|
if context.Bool("stats") {
|
|
s, err := container.Stats()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
events <- &types.Event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)}
|
|
close(events)
|
|
group.Wait()
|
|
return nil
|
|
}
|
|
go func() {
|
|
for range time.Tick(context.Duration("interval")) {
|
|
s, err := container.Stats()
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
continue
|
|
}
|
|
stats <- s
|
|
}
|
|
}()
|
|
n, err := container.NotifyOOM()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for {
|
|
select {
|
|
case _, ok := <-n:
|
|
if ok {
|
|
// this means an oom event was received, if it is !ok then
|
|
// the channel was closed because the container stopped and
|
|
// the cgroups no longer exist.
|
|
events <- &types.Event{Type: "oom", ID: container.ID()}
|
|
} else {
|
|
n = nil
|
|
}
|
|
case s := <-stats:
|
|
events <- &types.Event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)}
|
|
}
|
|
if n == nil {
|
|
close(events)
|
|
break
|
|
}
|
|
}
|
|
group.Wait()
|
|
return nil
|
|
},
|
|
}
|
|
|
|
func convertLibcontainerStats(ls *libcontainer.Stats) *types.Stats {
|
|
cg := ls.CgroupStats
|
|
if cg == nil {
|
|
return nil
|
|
}
|
|
var s types.Stats
|
|
s.Pids.Current = cg.PidsStats.Current
|
|
s.Pids.Limit = cg.PidsStats.Limit
|
|
|
|
s.CPU.Usage.Kernel = cg.CpuStats.CpuUsage.UsageInKernelmode
|
|
s.CPU.Usage.User = cg.CpuStats.CpuUsage.UsageInUsermode
|
|
s.CPU.Usage.Total = cg.CpuStats.CpuUsage.TotalUsage
|
|
s.CPU.Usage.Percpu = cg.CpuStats.CpuUsage.PercpuUsage
|
|
s.CPU.Usage.PercpuKernel = cg.CpuStats.CpuUsage.PercpuUsageInKernelmode
|
|
s.CPU.Usage.PercpuUser = cg.CpuStats.CpuUsage.PercpuUsageInUsermode
|
|
s.CPU.Throttling.Periods = cg.CpuStats.ThrottlingData.Periods
|
|
s.CPU.Throttling.ThrottledPeriods = cg.CpuStats.ThrottlingData.ThrottledPeriods
|
|
s.CPU.Throttling.ThrottledTime = cg.CpuStats.ThrottlingData.ThrottledTime
|
|
s.CPU.PSI = cg.CpuStats.PSI
|
|
|
|
s.CPUSet = types.CPUSet(cg.CPUSetStats)
|
|
|
|
s.Memory.Cache = cg.MemoryStats.Cache
|
|
s.Memory.Kernel = convertMemoryEntry(cg.MemoryStats.KernelUsage)
|
|
s.Memory.KernelTCP = convertMemoryEntry(cg.MemoryStats.KernelTCPUsage)
|
|
s.Memory.Swap = convertMemoryEntry(cg.MemoryStats.SwapUsage)
|
|
s.Memory.Usage = convertMemoryEntry(cg.MemoryStats.Usage)
|
|
s.Memory.Raw = cg.MemoryStats.Stats
|
|
s.Memory.PSI = cg.MemoryStats.PSI
|
|
|
|
s.Blkio.IoServiceBytesRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceBytesRecursive)
|
|
s.Blkio.IoServicedRecursive = convertBlkioEntry(cg.BlkioStats.IoServicedRecursive)
|
|
s.Blkio.IoQueuedRecursive = convertBlkioEntry(cg.BlkioStats.IoQueuedRecursive)
|
|
s.Blkio.IoServiceTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceTimeRecursive)
|
|
s.Blkio.IoWaitTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoWaitTimeRecursive)
|
|
s.Blkio.IoMergedRecursive = convertBlkioEntry(cg.BlkioStats.IoMergedRecursive)
|
|
s.Blkio.IoTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoTimeRecursive)
|
|
s.Blkio.SectorsRecursive = convertBlkioEntry(cg.BlkioStats.SectorsRecursive)
|
|
s.Blkio.PSI = cg.BlkioStats.PSI
|
|
|
|
s.Hugetlb = make(map[string]types.Hugetlb)
|
|
for k, v := range cg.HugetlbStats {
|
|
s.Hugetlb[k] = convertHugtlb(v)
|
|
}
|
|
|
|
if is := ls.IntelRdtStats; is != nil {
|
|
if intelrdt.IsCATEnabled() {
|
|
s.IntelRdt.L3CacheInfo = convertL3CacheInfo(is.L3CacheInfo)
|
|
s.IntelRdt.L3CacheSchemaRoot = is.L3CacheSchemaRoot
|
|
s.IntelRdt.L3CacheSchema = is.L3CacheSchema
|
|
}
|
|
if intelrdt.IsMBAEnabled() {
|
|
s.IntelRdt.MemBwInfo = convertMemBwInfo(is.MemBwInfo)
|
|
s.IntelRdt.MemBwSchemaRoot = is.MemBwSchemaRoot
|
|
s.IntelRdt.MemBwSchema = is.MemBwSchema
|
|
}
|
|
if intelrdt.IsMBMEnabled() {
|
|
s.IntelRdt.MBMStats = is.MBMStats
|
|
}
|
|
if intelrdt.IsCMTEnabled() {
|
|
s.IntelRdt.CMTStats = is.CMTStats
|
|
}
|
|
|
|
s.IntelRdt.Schemata = is.Schemata
|
|
}
|
|
|
|
s.NetworkInterfaces = ls.Interfaces
|
|
return &s
|
|
}
|
|
|
|
func convertHugtlb(c cgroups.HugetlbStats) types.Hugetlb {
|
|
return types.Hugetlb{
|
|
Usage: c.Usage,
|
|
Max: c.MaxUsage,
|
|
Failcnt: c.Failcnt,
|
|
}
|
|
}
|
|
|
|
func convertMemoryEntry(c cgroups.MemoryData) types.MemoryEntry {
|
|
return types.MemoryEntry{
|
|
Limit: c.Limit,
|
|
Usage: c.Usage,
|
|
Max: c.MaxUsage,
|
|
Failcnt: c.Failcnt,
|
|
}
|
|
}
|
|
|
|
func convertBlkioEntry(c []cgroups.BlkioStatEntry) []types.BlkioEntry {
|
|
out := make([]types.BlkioEntry, 0, len(c))
|
|
for _, e := range c {
|
|
out = append(out, types.BlkioEntry(e))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func convertL3CacheInfo(i *intelrdt.L3CacheInfo) *types.L3CacheInfo {
|
|
ci := types.L3CacheInfo(*i)
|
|
return &ci
|
|
}
|
|
|
|
func convertMemBwInfo(i *intelrdt.MemBwInfo) *types.MemBwInfo {
|
|
mi := types.MemBwInfo(*i)
|
|
return &mi
|
|
}
|