github.com/rawahars/moby@v24.0.4+incompatible/daemon/stats_unix.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package daemon // import "github.com/docker/docker/daemon" 5 6 import ( 7 "context" 8 "strings" 9 10 statsV1 "github.com/containerd/cgroups/v3/cgroup1/stats" 11 statsV2 "github.com/containerd/cgroups/v3/cgroup2/stats" 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/container" 14 "github.com/pkg/errors" 15 ) 16 17 func copyBlkioEntry(entries []*statsV1.BlkIOEntry) []types.BlkioStatEntry { 18 out := make([]types.BlkioStatEntry, len(entries)) 19 for i, re := range entries { 20 out[i] = types.BlkioStatEntry{ 21 Major: re.Major, 22 Minor: re.Minor, 23 Op: re.Op, 24 Value: re.Value, 25 } 26 } 27 return out 28 } 29 30 func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { 31 c.Lock() 32 task, err := c.GetRunningTask() 33 c.Unlock() 34 if err != nil { 35 return nil, err 36 } 37 cs, err := task.Stats(context.Background()) 38 if err != nil { 39 if strings.Contains(err.Error(), "container not found") { 40 return nil, containerNotFound(c.ID) 41 } 42 return nil, err 43 } 44 s := &types.StatsJSON{} 45 s.Read = cs.Read 46 stats := cs.Metrics 47 switch t := stats.(type) { 48 case *statsV1.Metrics: 49 return daemon.statsV1(s, t) 50 case *statsV2.Metrics: 51 return daemon.statsV2(s, t) 52 default: 53 return nil, errors.Errorf("unexpected type of metrics %+v", t) 54 } 55 } 56 57 func (daemon *Daemon) statsV1(s *types.StatsJSON, stats *statsV1.Metrics) (*types.StatsJSON, error) { 58 if stats.Blkio != nil { 59 s.BlkioStats = types.BlkioStats{ 60 IoServiceBytesRecursive: copyBlkioEntry(stats.Blkio.IoServiceBytesRecursive), 61 IoServicedRecursive: copyBlkioEntry(stats.Blkio.IoServicedRecursive), 62 IoQueuedRecursive: copyBlkioEntry(stats.Blkio.IoQueuedRecursive), 63 IoServiceTimeRecursive: copyBlkioEntry(stats.Blkio.IoServiceTimeRecursive), 64 IoWaitTimeRecursive: copyBlkioEntry(stats.Blkio.IoWaitTimeRecursive), 65 IoMergedRecursive: copyBlkioEntry(stats.Blkio.IoMergedRecursive), 66 IoTimeRecursive: copyBlkioEntry(stats.Blkio.IoTimeRecursive), 67 SectorsRecursive: copyBlkioEntry(stats.Blkio.SectorsRecursive), 68 } 69 } 70 if stats.CPU != nil { 71 s.CPUStats = types.CPUStats{ 72 CPUUsage: types.CPUUsage{ 73 TotalUsage: stats.CPU.Usage.Total, 74 PercpuUsage: stats.CPU.Usage.PerCPU, 75 UsageInKernelmode: stats.CPU.Usage.Kernel, 76 UsageInUsermode: stats.CPU.Usage.User, 77 }, 78 ThrottlingData: types.ThrottlingData{ 79 Periods: stats.CPU.Throttling.Periods, 80 ThrottledPeriods: stats.CPU.Throttling.ThrottledPeriods, 81 ThrottledTime: stats.CPU.Throttling.ThrottledTime, 82 }, 83 } 84 } 85 86 if stats.Memory != nil { 87 raw := map[string]uint64{ 88 "cache": stats.Memory.Cache, 89 "rss": stats.Memory.RSS, 90 "rss_huge": stats.Memory.RSSHuge, 91 "mapped_file": stats.Memory.MappedFile, 92 "dirty": stats.Memory.Dirty, 93 "writeback": stats.Memory.Writeback, 94 "pgpgin": stats.Memory.PgPgIn, 95 "pgpgout": stats.Memory.PgPgOut, 96 "pgfault": stats.Memory.PgFault, 97 "pgmajfault": stats.Memory.PgMajFault, 98 "inactive_anon": stats.Memory.InactiveAnon, 99 "active_anon": stats.Memory.ActiveAnon, 100 "inactive_file": stats.Memory.InactiveFile, 101 "active_file": stats.Memory.ActiveFile, 102 "unevictable": stats.Memory.Unevictable, 103 "hierarchical_memory_limit": stats.Memory.HierarchicalMemoryLimit, 104 "hierarchical_memsw_limit": stats.Memory.HierarchicalSwapLimit, 105 "total_cache": stats.Memory.TotalCache, 106 "total_rss": stats.Memory.TotalRSS, 107 "total_rss_huge": stats.Memory.TotalRSSHuge, 108 "total_mapped_file": stats.Memory.TotalMappedFile, 109 "total_dirty": stats.Memory.TotalDirty, 110 "total_writeback": stats.Memory.TotalWriteback, 111 "total_pgpgin": stats.Memory.TotalPgPgIn, 112 "total_pgpgout": stats.Memory.TotalPgPgOut, 113 "total_pgfault": stats.Memory.TotalPgFault, 114 "total_pgmajfault": stats.Memory.TotalPgMajFault, 115 "total_inactive_anon": stats.Memory.TotalInactiveAnon, 116 "total_active_anon": stats.Memory.TotalActiveAnon, 117 "total_inactive_file": stats.Memory.TotalInactiveFile, 118 "total_active_file": stats.Memory.TotalActiveFile, 119 "total_unevictable": stats.Memory.TotalUnevictable, 120 } 121 if stats.Memory.Usage != nil { 122 s.MemoryStats = types.MemoryStats{ 123 Stats: raw, 124 Usage: stats.Memory.Usage.Usage, 125 MaxUsage: stats.Memory.Usage.Max, 126 Limit: stats.Memory.Usage.Limit, 127 Failcnt: stats.Memory.Usage.Failcnt, 128 } 129 } else { 130 s.MemoryStats = types.MemoryStats{ 131 Stats: raw, 132 } 133 } 134 135 // if the container does not set memory limit, use the machineMemory 136 if s.MemoryStats.Limit > daemon.machineMemory && daemon.machineMemory > 0 { 137 s.MemoryStats.Limit = daemon.machineMemory 138 } 139 } 140 141 if stats.Pids != nil { 142 s.PidsStats = types.PidsStats{ 143 Current: stats.Pids.Current, 144 Limit: stats.Pids.Limit, 145 } 146 } 147 148 return s, nil 149 } 150 151 func (daemon *Daemon) statsV2(s *types.StatsJSON, stats *statsV2.Metrics) (*types.StatsJSON, error) { 152 if stats.Io != nil { 153 var isbr []types.BlkioStatEntry 154 for _, re := range stats.Io.Usage { 155 isbr = append(isbr, 156 types.BlkioStatEntry{ 157 Major: re.Major, 158 Minor: re.Minor, 159 Op: "read", 160 Value: re.Rbytes, 161 }, 162 types.BlkioStatEntry{ 163 Major: re.Major, 164 Minor: re.Minor, 165 Op: "write", 166 Value: re.Wbytes, 167 }, 168 ) 169 } 170 s.BlkioStats = types.BlkioStats{ 171 IoServiceBytesRecursive: isbr, 172 // Other fields are unsupported 173 } 174 } 175 176 if stats.CPU != nil { 177 s.CPUStats = types.CPUStats{ 178 CPUUsage: types.CPUUsage{ 179 TotalUsage: stats.CPU.UsageUsec * 1000, 180 // PercpuUsage is not supported 181 UsageInKernelmode: stats.CPU.SystemUsec * 1000, 182 UsageInUsermode: stats.CPU.UserUsec * 1000, 183 }, 184 ThrottlingData: types.ThrottlingData{ 185 Periods: stats.CPU.NrPeriods, 186 ThrottledPeriods: stats.CPU.NrThrottled, 187 ThrottledTime: stats.CPU.ThrottledUsec * 1000, 188 }, 189 } 190 } 191 192 if stats.Memory != nil { 193 s.MemoryStats = types.MemoryStats{ 194 // Stats is not compatible with v1 195 Stats: map[string]uint64{ 196 "anon": stats.Memory.Anon, 197 "file": stats.Memory.File, 198 "kernel_stack": stats.Memory.KernelStack, 199 "slab": stats.Memory.Slab, 200 "sock": stats.Memory.Sock, 201 "shmem": stats.Memory.Shmem, 202 "file_mapped": stats.Memory.FileMapped, 203 "file_dirty": stats.Memory.FileDirty, 204 "file_writeback": stats.Memory.FileWriteback, 205 "anon_thp": stats.Memory.AnonThp, 206 "inactive_anon": stats.Memory.InactiveAnon, 207 "active_anon": stats.Memory.ActiveAnon, 208 "inactive_file": stats.Memory.InactiveFile, 209 "active_file": stats.Memory.ActiveFile, 210 "unevictable": stats.Memory.Unevictable, 211 "slab_reclaimable": stats.Memory.SlabReclaimable, 212 "slab_unreclaimable": stats.Memory.SlabUnreclaimable, 213 "pgfault": stats.Memory.Pgfault, 214 "pgmajfault": stats.Memory.Pgmajfault, 215 "workingset_refault": stats.Memory.WorkingsetRefault, 216 "workingset_activate": stats.Memory.WorkingsetActivate, 217 "workingset_nodereclaim": stats.Memory.WorkingsetNodereclaim, 218 "pgrefill": stats.Memory.Pgrefill, 219 "pgscan": stats.Memory.Pgscan, 220 "pgsteal": stats.Memory.Pgsteal, 221 "pgactivate": stats.Memory.Pgactivate, 222 "pgdeactivate": stats.Memory.Pgdeactivate, 223 "pglazyfree": stats.Memory.Pglazyfree, 224 "pglazyfreed": stats.Memory.Pglazyfreed, 225 "thp_fault_alloc": stats.Memory.ThpFaultAlloc, 226 "thp_collapse_alloc": stats.Memory.ThpCollapseAlloc, 227 }, 228 Usage: stats.Memory.Usage, 229 // MaxUsage is not supported 230 Limit: stats.Memory.UsageLimit, 231 } 232 // if the container does not set memory limit, use the machineMemory 233 if s.MemoryStats.Limit > daemon.machineMemory && daemon.machineMemory > 0 { 234 s.MemoryStats.Limit = daemon.machineMemory 235 } 236 if stats.MemoryEvents != nil { 237 // Failcnt is set to the "oom" field of the "memory.events" file. 238 // See https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html 239 s.MemoryStats.Failcnt = stats.MemoryEvents.Oom 240 } 241 } 242 243 if stats.Pids != nil { 244 s.PidsStats = types.PidsStats{ 245 Current: stats.Pids.Current, 246 Limit: stats.Pids.Limit, 247 } 248 } 249 250 return s, nil 251 } 252 253 // Resolve Network SandboxID in case the container reuse another container's network stack 254 func (daemon *Daemon) getNetworkSandboxID(c *container.Container) (string, error) { 255 curr := c 256 for curr.HostConfig.NetworkMode.IsContainer() { 257 containerID := curr.HostConfig.NetworkMode.ConnectedContainer() 258 connected, err := daemon.GetContainer(containerID) 259 if err != nil { 260 return "", errors.Wrapf(err, "Could not get container for %s", containerID) 261 } 262 curr = connected 263 } 264 return curr.NetworkSettings.SandboxID, nil 265 } 266 267 func (daemon *Daemon) getNetworkStats(c *container.Container) (map[string]types.NetworkStats, error) { 268 sandboxID, err := daemon.getNetworkSandboxID(c) 269 if err != nil { 270 return nil, err 271 } 272 273 sb, err := daemon.netController.SandboxByID(sandboxID) 274 if err != nil { 275 return nil, err 276 } 277 278 lnstats, err := sb.Statistics() 279 if err != nil { 280 return nil, err 281 } 282 283 stats := make(map[string]types.NetworkStats) 284 // Convert libnetwork nw stats into api stats 285 for ifName, ifStats := range lnstats { 286 stats[ifName] = types.NetworkStats{ 287 RxBytes: ifStats.RxBytes, 288 RxPackets: ifStats.RxPackets, 289 RxErrors: ifStats.RxErrors, 290 RxDropped: ifStats.RxDropped, 291 TxBytes: ifStats.TxBytes, 292 TxPackets: ifStats.TxPackets, 293 TxErrors: ifStats.TxErrors, 294 TxDropped: ifStats.TxDropped, 295 } 296 } 297 298 return stats, nil 299 }