...

Source file src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go

Documentation: github.com/opencontainers/runc/libcontainer/cgroups/fs

     1  package fs
     2  
     3  import (
     4  	"bufio"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/opencontainers/runc/libcontainer/cgroups"
    10  	"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
    11  	"github.com/opencontainers/runc/libcontainer/configs"
    12  )
    13  
    14  const (
    15  	cgroupCpuacctStat     = "cpuacct.stat"
    16  	cgroupCpuacctUsageAll = "cpuacct.usage_all"
    17  
    18  	nanosecondsInSecond = 1000000000
    19  
    20  	userModeColumn              = 1
    21  	kernelModeColumn            = 2
    22  	cuacctUsageAllColumnsNumber = 3
    23  
    24  	// The value comes from `C.sysconf(C._SC_CLK_TCK)`, and
    25  	// on Linux it's a constant which is safe to be hard coded,
    26  	// so we can avoid using cgo here. For details, see:
    27  	// https://github.com/containerd/cgroups/pull/12
    28  	clockTicks uint64 = 100
    29  )
    30  
    31  type CpuacctGroup struct{}
    32  
    33  func (s *CpuacctGroup) Name() string {
    34  	return "cpuacct"
    35  }
    36  
    37  func (s *CpuacctGroup) Apply(path string, _ *configs.Resources, pid int) error {
    38  	return apply(path, pid)
    39  }
    40  
    41  func (s *CpuacctGroup) Set(_ string, _ *configs.Resources) error {
    42  	return nil
    43  }
    44  
    45  func (s *CpuacctGroup) GetStats(path string, stats *cgroups.Stats) error {
    46  	if !cgroups.PathExists(path) {
    47  		return nil
    48  	}
    49  	userModeUsage, kernelModeUsage, err := getCpuUsageBreakdown(path)
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	totalUsage, err := fscommon.GetCgroupParamUint(path, "cpuacct.usage")
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	percpuUsage, err := getPercpuUsage(path)
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	percpuUsageInKernelmode, percpuUsageInUsermode, err := getPercpuUsageInModes(path)
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	stats.CpuStats.CpuUsage.TotalUsage = totalUsage
    70  	stats.CpuStats.CpuUsage.PercpuUsage = percpuUsage
    71  	stats.CpuStats.CpuUsage.PercpuUsageInKernelmode = percpuUsageInKernelmode
    72  	stats.CpuStats.CpuUsage.PercpuUsageInUsermode = percpuUsageInUsermode
    73  	stats.CpuStats.CpuUsage.UsageInUsermode = userModeUsage
    74  	stats.CpuStats.CpuUsage.UsageInKernelmode = kernelModeUsage
    75  	return nil
    76  }
    77  
    78  // Returns user and kernel usage breakdown in nanoseconds.
    79  func getCpuUsageBreakdown(path string) (uint64, uint64, error) {
    80  	var userModeUsage, kernelModeUsage uint64
    81  	const (
    82  		userField   = "user"
    83  		systemField = "system"
    84  		file        = cgroupCpuacctStat
    85  	)
    86  
    87  	// Expected format:
    88  	// user <usage in ticks>
    89  	// system <usage in ticks>
    90  	data, err := cgroups.ReadFile(path, file)
    91  	if err != nil {
    92  		return 0, 0, err
    93  	}
    94  	// TODO: use strings.SplitN instead.
    95  	fields := strings.Fields(data)
    96  	if len(fields) < 4 || fields[0] != userField || fields[2] != systemField {
    97  		return 0, 0, malformedLine(path, file, data)
    98  	}
    99  	if userModeUsage, err = strconv.ParseUint(fields[1], 10, 64); err != nil {
   100  		return 0, 0, &parseError{Path: path, File: file, Err: err}
   101  	}
   102  	if kernelModeUsage, err = strconv.ParseUint(fields[3], 10, 64); err != nil {
   103  		return 0, 0, &parseError{Path: path, File: file, Err: err}
   104  	}
   105  
   106  	return (userModeUsage * nanosecondsInSecond) / clockTicks, (kernelModeUsage * nanosecondsInSecond) / clockTicks, nil
   107  }
   108  
   109  func getPercpuUsage(path string) ([]uint64, error) {
   110  	const file = "cpuacct.usage_percpu"
   111  	percpuUsage := []uint64{}
   112  	data, err := cgroups.ReadFile(path, file)
   113  	if err != nil {
   114  		return percpuUsage, err
   115  	}
   116  	// TODO: use strings.SplitN instead.
   117  	for _, value := range strings.Fields(data) {
   118  		value, err := strconv.ParseUint(value, 10, 64)
   119  		if err != nil {
   120  			return percpuUsage, &parseError{Path: path, File: file, Err: err}
   121  		}
   122  		percpuUsage = append(percpuUsage, value)
   123  	}
   124  	return percpuUsage, nil
   125  }
   126  
   127  func getPercpuUsageInModes(path string) ([]uint64, []uint64, error) {
   128  	usageKernelMode := []uint64{}
   129  	usageUserMode := []uint64{}
   130  	const file = cgroupCpuacctUsageAll
   131  
   132  	fd, err := cgroups.OpenFile(path, file, os.O_RDONLY)
   133  	if os.IsNotExist(err) {
   134  		return usageKernelMode, usageUserMode, nil
   135  	} else if err != nil {
   136  		return nil, nil, err
   137  	}
   138  	defer fd.Close()
   139  
   140  	scanner := bufio.NewScanner(fd)
   141  	scanner.Scan() // skipping header line
   142  
   143  	for scanner.Scan() {
   144  		lineFields := strings.SplitN(scanner.Text(), " ", cuacctUsageAllColumnsNumber+1)
   145  		if len(lineFields) != cuacctUsageAllColumnsNumber {
   146  			continue
   147  		}
   148  
   149  		usageInKernelMode, err := strconv.ParseUint(lineFields[kernelModeColumn], 10, 64)
   150  		if err != nil {
   151  			return nil, nil, &parseError{Path: path, File: file, Err: err}
   152  		}
   153  		usageKernelMode = append(usageKernelMode, usageInKernelMode)
   154  
   155  		usageInUserMode, err := strconv.ParseUint(lineFields[userModeColumn], 10, 64)
   156  		if err != nil {
   157  			return nil, nil, &parseError{Path: path, File: file, Err: err}
   158  		}
   159  		usageUserMode = append(usageUserMode, usageInUserMode)
   160  	}
   161  	if err := scanner.Err(); err != nil {
   162  		return nil, nil, &parseError{Path: path, File: file, Err: err}
   163  	}
   164  
   165  	return usageKernelMode, usageUserMode, nil
   166  }
   167  

View as plain text