github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/events.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "os" 8 "sync" 9 "time" 10 11 "github.com/opencontainers/runc/libcontainer" 12 "github.com/opencontainers/runc/libcontainer/cgroups" 13 "github.com/opencontainers/runc/libcontainer/intelrdt" 14 "github.com/opencontainers/runc/types" 15 16 "github.com/sirupsen/logrus" 17 "github.com/urfave/cli" 18 ) 19 20 var eventsCommand = cli.Command{ 21 Name: "events", 22 Usage: "display container events such as OOM notifications, cpu, memory, and IO usage statistics", 23 ArgsUsage: `<container-id> 24 25 Where "<container-id>" is the name for the instance of the container.`, 26 Description: `The events command displays information about the container. By default the 27 information is displayed once every 5 seconds.`, 28 Flags: []cli.Flag{ 29 cli.DurationFlag{Name: "interval", Value: 5 * time.Second, Usage: "set the stats collection interval"}, 30 cli.BoolFlag{Name: "stats", Usage: "display the container's stats then exit"}, 31 }, 32 Action: func(context *cli.Context) error { 33 if err := checkArgs(context, 1, exactArgs); err != nil { 34 return err 35 } 36 container, err := getContainer(context) 37 if err != nil { 38 return err 39 } 40 duration := context.Duration("interval") 41 if duration <= 0 { 42 return errors.New("duration interval must be greater than 0") 43 } 44 status, err := container.Status() 45 if err != nil { 46 return err 47 } 48 if status == libcontainer.Stopped { 49 return fmt.Errorf("container with id %s is not running", container.ID()) 50 } 51 var ( 52 stats = make(chan *libcontainer.Stats, 1) 53 events = make(chan *types.Event, 1024) 54 group = &sync.WaitGroup{} 55 ) 56 group.Add(1) 57 go func() { 58 defer group.Done() 59 enc := json.NewEncoder(os.Stdout) 60 for e := range events { 61 if err := enc.Encode(e); err != nil { 62 logrus.Error(err) 63 } 64 } 65 }() 66 if context.Bool("stats") { 67 s, err := container.Stats() 68 if err != nil { 69 return err 70 } 71 events <- &types.Event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)} 72 close(events) 73 group.Wait() 74 return nil 75 } 76 go func() { 77 for range time.Tick(context.Duration("interval")) { 78 s, err := container.Stats() 79 if err != nil { 80 logrus.Error(err) 81 continue 82 } 83 stats <- s 84 } 85 }() 86 n, err := container.NotifyOOM() 87 if err != nil { 88 return err 89 } 90 for { 91 select { 92 case _, ok := <-n: 93 if ok { 94 // this means an oom event was received, if it is !ok then 95 // the channel was closed because the container stopped and 96 // the cgroups no longer exist. 97 events <- &types.Event{Type: "oom", ID: container.ID()} 98 } else { 99 n = nil 100 } 101 case s := <-stats: 102 events <- &types.Event{Type: "stats", ID: container.ID(), Data: convertLibcontainerStats(s)} 103 } 104 if n == nil { 105 close(events) 106 break 107 } 108 } 109 group.Wait() 110 return nil 111 }, 112 } 113 114 func convertLibcontainerStats(ls *libcontainer.Stats) *types.Stats { 115 cg := ls.CgroupStats 116 if cg == nil { 117 return nil 118 } 119 var s types.Stats 120 s.Pids.Current = cg.PidsStats.Current 121 s.Pids.Limit = cg.PidsStats.Limit 122 123 s.CPU.Usage.Kernel = cg.CpuStats.CpuUsage.UsageInKernelmode 124 s.CPU.Usage.User = cg.CpuStats.CpuUsage.UsageInUsermode 125 s.CPU.Usage.Total = cg.CpuStats.CpuUsage.TotalUsage 126 s.CPU.Usage.Percpu = cg.CpuStats.CpuUsage.PercpuUsage 127 s.CPU.Usage.PercpuKernel = cg.CpuStats.CpuUsage.PercpuUsageInKernelmode 128 s.CPU.Usage.PercpuUser = cg.CpuStats.CpuUsage.PercpuUsageInUsermode 129 s.CPU.Throttling.Periods = cg.CpuStats.ThrottlingData.Periods 130 s.CPU.Throttling.ThrottledPeriods = cg.CpuStats.ThrottlingData.ThrottledPeriods 131 s.CPU.Throttling.ThrottledTime = cg.CpuStats.ThrottlingData.ThrottledTime 132 s.CPU.PSI = cg.CpuStats.PSI 133 134 s.CPUSet = types.CPUSet(cg.CPUSetStats) 135 136 s.Memory.Cache = cg.MemoryStats.Cache 137 s.Memory.Kernel = convertMemoryEntry(cg.MemoryStats.KernelUsage) 138 s.Memory.KernelTCP = convertMemoryEntry(cg.MemoryStats.KernelTCPUsage) 139 s.Memory.Swap = convertMemoryEntry(cg.MemoryStats.SwapUsage) 140 s.Memory.Usage = convertMemoryEntry(cg.MemoryStats.Usage) 141 s.Memory.Raw = cg.MemoryStats.Stats 142 s.Memory.PSI = cg.MemoryStats.PSI 143 144 s.Blkio.IoServiceBytesRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceBytesRecursive) 145 s.Blkio.IoServicedRecursive = convertBlkioEntry(cg.BlkioStats.IoServicedRecursive) 146 s.Blkio.IoQueuedRecursive = convertBlkioEntry(cg.BlkioStats.IoQueuedRecursive) 147 s.Blkio.IoServiceTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceTimeRecursive) 148 s.Blkio.IoWaitTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoWaitTimeRecursive) 149 s.Blkio.IoMergedRecursive = convertBlkioEntry(cg.BlkioStats.IoMergedRecursive) 150 s.Blkio.IoTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoTimeRecursive) 151 s.Blkio.SectorsRecursive = convertBlkioEntry(cg.BlkioStats.SectorsRecursive) 152 s.Blkio.PSI = cg.BlkioStats.PSI 153 154 s.Hugetlb = make(map[string]types.Hugetlb) 155 for k, v := range cg.HugetlbStats { 156 s.Hugetlb[k] = convertHugtlb(v) 157 } 158 159 if is := ls.IntelRdtStats; is != nil { 160 if intelrdt.IsCATEnabled() { 161 s.IntelRdt.L3CacheInfo = convertL3CacheInfo(is.L3CacheInfo) 162 s.IntelRdt.L3CacheSchemaRoot = is.L3CacheSchemaRoot 163 s.IntelRdt.L3CacheSchema = is.L3CacheSchema 164 } 165 if intelrdt.IsMBAEnabled() { 166 s.IntelRdt.MemBwInfo = convertMemBwInfo(is.MemBwInfo) 167 s.IntelRdt.MemBwSchemaRoot = is.MemBwSchemaRoot 168 s.IntelRdt.MemBwSchema = is.MemBwSchema 169 } 170 if intelrdt.IsMBMEnabled() { 171 s.IntelRdt.MBMStats = is.MBMStats 172 } 173 if intelrdt.IsCMTEnabled() { 174 s.IntelRdt.CMTStats = is.CMTStats 175 } 176 } 177 178 s.NetworkInterfaces = ls.Interfaces 179 return &s 180 } 181 182 func convertHugtlb(c cgroups.HugetlbStats) types.Hugetlb { 183 return types.Hugetlb{ 184 Usage: c.Usage, 185 Max: c.MaxUsage, 186 Failcnt: c.Failcnt, 187 } 188 } 189 190 func convertMemoryEntry(c cgroups.MemoryData) types.MemoryEntry { 191 return types.MemoryEntry{ 192 Limit: c.Limit, 193 Usage: c.Usage, 194 Max: c.MaxUsage, 195 Failcnt: c.Failcnt, 196 } 197 } 198 199 func convertBlkioEntry(c []cgroups.BlkioStatEntry) []types.BlkioEntry { 200 var out []types.BlkioEntry 201 for _, e := range c { 202 out = append(out, types.BlkioEntry(e)) 203 } 204 return out 205 } 206 207 func convertL3CacheInfo(i *intelrdt.L3CacheInfo) *types.L3CacheInfo { 208 ci := types.L3CacheInfo(*i) 209 return &ci 210 } 211 212 func convertMemBwInfo(i *intelrdt.MemBwInfo) *types.MemBwInfo { 213 mi := types.MemBwInfo(*i) 214 return &mi 215 }