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  }