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
25
26
27
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
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
88
89
90 data, err := cgroups.ReadFile(path, file)
91 if err != nil {
92 return 0, 0, err
93 }
94
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
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()
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