Skip to content

Commit 1c8943e

Browse files
committed
implement cgroupv1 cpu
1 parent 3643407 commit 1c8943e

File tree

1 file changed

+73
-23
lines changed

1 file changed

+73
-23
lines changed

cli/clistat/cgroup.go

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import (
1212
)
1313

1414
const (
15-
cgroupV2CPUMax = "/sys/fs/cgroup/cpu.max"
16-
cgroupV2CPUStat = "/sys/fs/cgroup/cpu.stat"
15+
cgroupV1CPUAcctUsage = "/sys/fs/cgroup/cpu,cpuacct/cpuacct.usage"
16+
cgroupV1CFSQuotaUs = "/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us"
17+
cgroupV2CPUMax = "/sys/fs/cgroup/cpu.max"
18+
cgroupV2CPUStat = "/sys/fs/cgroup/cpu.stat"
1719
)
1820

1921
// ContainerCPU returns the CPU usage of the container cgroup.
@@ -38,8 +40,8 @@ func (s *Statter) ContainerCPU() (*Result, error) {
3840

3941
r := &Result{
4042
Unit: "cores",
41-
Used: (used2 - used1).Seconds(),
42-
Total: ptr.To(total.Seconds() / s.sampleInterval.Seconds()), // close enough to the truth
43+
Used: (used2 - used1).Seconds() * s.sampleInterval.Seconds(),
44+
Total: ptr.To(total.Seconds()), // close enough to the truth
4345
}
4446
return r, nil
4547
}
@@ -62,12 +64,12 @@ func (s *Statter) isCGroupV2() bool {
6264
func (s *Statter) cGroupV2CPU() (used, total time.Duration, err error) {
6365
total, err = s.cGroupv2CPUTotal()
6466
if err != nil {
65-
return 0, 0, xerrors.Errorf("get cgroup v2 CPU cores: %w", err)
67+
return 0, 0, xerrors.Errorf("get cgroup v2 cpu total: %w", err)
6668
}
6769

6870
used, err = s.cGroupv2CPUUsed()
6971
if err != nil {
70-
return 0, 0, xerrors.Errorf("get cgroup v2 CPU used: %w", err)
72+
return 0, 0, xerrors.Errorf("get cgroup v2 cpu used: %w", err)
7173
}
7274

7375
return used, total, nil
@@ -77,7 +79,7 @@ func (s *Statter) cGroupv2CPUUsed() (used time.Duration, err error) {
7779
var data []byte
7880
data, err = afero.ReadFile(s.fs, cgroupV2CPUStat)
7981
if err != nil {
80-
return 0, xerrors.Errorf("read /sys/fs/cgroup/cpu.stat: %w", err)
82+
return 0, xerrors.Errorf("read %s: %w", cgroupV2CPUStat, err)
8183
}
8284

8385
bs := bufio.NewScanner(bytes.NewReader(data))
@@ -89,54 +91,102 @@ func (s *Statter) cGroupv2CPUUsed() (used time.Duration, err error) {
8991

9092
parts := bytes.Split(line, []byte(" "))
9193
if len(parts) != 2 {
92-
return 0, xerrors.Errorf("unexpected line in /sys/fs/cgroup/cpu.stat: %s", line)
94+
return 0, xerrors.Errorf("unexpected line in %s: %s", cgroupV2CPUStat, line)
9395
}
9496

9597
iused, err := strconv.Atoi(string(parts[1]))
9698
if err != nil {
97-
return 0, xerrors.Errorf("parse /sys/fs/cgroup/cpu.stat: %w", err)
99+
return 0, xerrors.Errorf("parse %s: %w", err, cgroupV2CPUStat)
98100
}
99101

100102
return time.Duration(iused) * time.Microsecond, nil
101103
}
102104

103-
return 0, xerrors.Errorf("did not find expected usage_usec in /sys/fs/cgroup/cpu.stat")
105+
return 0, xerrors.Errorf("did not find expected usage_usec in %s", cgroupV2CPUStat)
104106
}
105107

106108
func (s *Statter) cGroupv2CPUTotal() (total time.Duration, err error) {
107109
var data []byte
108-
var quotaUs int
109-
data, err = afero.ReadFile(s.fs, "/sys/fs/cgroup/cpu.max")
110+
var quotaUs int64
111+
data, err = afero.ReadFile(s.fs, cgroupV2CPUMax)
110112
if err != nil {
111-
return 0, xerrors.Errorf("read /sys/fs/cgroup/cpu.max: %w", err)
113+
return 0, xerrors.Errorf("read %s: %w", cgroupV2CPUMax, err)
112114
}
113115

114116
lines := bytes.Split(data, []byte("\n"))
115117
if len(lines) < 1 {
116-
return 0, xerrors.Errorf("unexpected empty /sys/fs/cgroup/cpu.max")
118+
return 0, xerrors.Errorf("unexpected empty %s", cgroupV2CPUMax)
117119
}
118120

119-
line := lines[0]
120-
parts := bytes.Split(line, []byte(" "))
121+
parts := bytes.Split(lines[0], []byte(" "))
121122
if len(parts) != 2 {
122-
return 0, xerrors.Errorf("unexpected line in /sys/fs/cgroup/cpu.max: %s", line)
123+
return 0, xerrors.Errorf("unexpected line in %s: %s", cgroupV2CPUMax, lines[0])
123124
}
124125

125126
if bytes.Equal(parts[0], []byte("max")) {
126-
quotaUs = s.nproc * int(time.Second.Microseconds())
127+
quotaUs = int64(s.nproc) * time.Second.Microseconds()
127128
} else {
128-
quotaUs, err = strconv.Atoi(string(parts[0]))
129+
quotaUs, err = strconv.ParseInt(string(parts[0]), 10, 64)
129130
if err != nil {
130-
return 0, xerrors.Errorf("parse /sys/fs/cgroup/cpu.max: %w", err)
131+
return 0, xerrors.Errorf("parse %s: %w", cgroupV2CPUMax, err)
131132
}
132133
}
133134

134135
return time.Duration(quotaUs) * time.Microsecond, nil
135136
}
136137

137-
func (*Statter) cGroupV1CPU() (time.Duration, time.Duration, error) {
138-
// TODO: implement
139-
return 0, 0, nil
138+
func (s *Statter) cGroupV1CPU() (used, total time.Duration, err error) {
139+
total, err = s.cGroupV1CPUTotal()
140+
if err != nil {
141+
return 0, 0, xerrors.Errorf("get cgroup v1 CPU total: %w", err)
142+
}
143+
144+
used, err = s.cgroupV1CPUUsed()
145+
if err != nil {
146+
return 0, 0, xerrors.Errorf("get cgruop v1 cpu used: %w", err)
147+
}
148+
149+
return used, total, nil
150+
}
151+
152+
func (s *Statter) cGroupV1CPUTotal() (time.Duration, error) {
153+
var data []byte
154+
var err error
155+
var quotaUs int64
156+
157+
data, err = afero.ReadFile(s.fs, cgroupV1CFSQuotaUs)
158+
if err != nil {
159+
return 0, xerrors.Errorf("read %s: %w", cgroupV1CFSQuotaUs, err)
160+
}
161+
162+
quotaUs, err = strconv.ParseInt(string(bytes.TrimSpace(data)), 10, 64)
163+
if err != nil {
164+
return 0, xerrors.Errorf("parse %s: %w", cgroupV1CFSQuotaUs, err)
165+
}
166+
167+
if quotaUs < 0 {
168+
quotaUs = int64(s.nproc) * time.Second.Microseconds()
169+
}
170+
171+
return time.Duration(quotaUs) * time.Microsecond, nil
172+
}
173+
174+
func (s *Statter) cgroupV1CPUUsed() (time.Duration, error) {
175+
var data []byte
176+
var err error
177+
var usageUs int64
178+
179+
data, err = afero.ReadFile(s.fs, cgroupV1CPUAcctUsage)
180+
if err != nil {
181+
return 0, xerrors.Errorf("read %s: %w", cgroupV1CPUAcctUsage, err)
182+
}
183+
184+
usageUs, err = strconv.ParseInt(string(bytes.TrimSpace(data)), 10, 64)
185+
if err != nil {
186+
return 0, xerrors.Errorf("parse %s: %w", cgroupV1CPUAcctUsage, err)
187+
}
188+
189+
return time.Duration(usageUs) * time.Microsecond, nil
140190
}
141191

142192
// ContainerMemory returns the memory usage of the container cgroup.

0 commit comments

Comments
 (0)