Source file
src/runtime/cgroup_linux_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "fmt"
9 "internal/cgrouptest"
10 "runtime"
11 "strings"
12 "syscall"
13 "testing"
14 "unsafe"
15 )
16
17 func mustHaveFourCPUs(t *testing.T) {
18
19
20
21
22
23 if runtime.NumCPU() < 4 {
24 t.Helper()
25 t.Skip("skipping test: fewer than 4 CPUs")
26 }
27 }
28
29 func TestCgroupGOMAXPROCS(t *testing.T) {
30 mustHaveFourCPUs(t)
31
32 exe, err := buildTestProg(t, "testprog")
33 if err != nil {
34 t.Fatal(err)
35 }
36
37 tests := []struct {
38 godebug int
39 want int
40 }{
41
42
43 {
44 godebug: 1,
45 want: 3,
46 },
47
48 {
49 godebug: 0,
50 want: runtime.NumCPU(),
51 },
52 }
53 for _, tc := range tests {
54 t.Run(fmt.Sprintf("containermaxprocs=%d", tc.godebug), func(t *testing.T) {
55 cgrouptest.InCgroupV2(t, func(c *cgrouptest.CgroupV2) {
56 if err := c.SetCPUMax(300000, 100000); err != nil {
57 t.Fatalf("unable to set CPU limit: %v", err)
58 }
59
60 got := runBuiltTestProg(t, exe, "PrintGOMAXPROCS", fmt.Sprintf("GODEBUG=containermaxprocs=%d", tc.godebug))
61 want := fmt.Sprintf("%d\n", tc.want)
62 if got != want {
63 t.Fatalf("output got %q want %q", got, want)
64 }
65 })
66 })
67 }
68 }
69
70
71 func TestCgroupGOMAXPROCSNoLimit(t *testing.T) {
72 exe, err := buildTestProg(t, "testprog")
73 if err != nil {
74 t.Fatal(err)
75 }
76
77 cgrouptest.InCgroupV2(t, func(c *cgrouptest.CgroupV2) {
78 if err := c.SetCPUMax(-1, 100000); err != nil {
79 t.Fatalf("unable to set CPU limit: %v", err)
80 }
81
82 got := runBuiltTestProg(t, exe, "PrintGOMAXPROCS")
83 want := fmt.Sprintf("%d\n", runtime.NumCPU())
84 if got != want {
85 t.Fatalf("output got %q want %q", got, want)
86 }
87 })
88 }
89
90
91 func TestCgroupGOMAXPROCSHigherThanNumCPU(t *testing.T) {
92 exe, err := buildTestProg(t, "testprog")
93 if err != nil {
94 t.Fatal(err)
95 }
96
97 cgrouptest.InCgroupV2(t, func(c *cgrouptest.CgroupV2) {
98 if err := c.SetCPUMax(2*int64(runtime.NumCPU())*100000, 100000); err != nil {
99 t.Fatalf("unable to set CPU limit: %v", err)
100 }
101
102 got := runBuiltTestProg(t, exe, "PrintGOMAXPROCS")
103 want := fmt.Sprintf("%d\n", runtime.NumCPU())
104 if got != want {
105 t.Fatalf("output got %q want %q", got, want)
106 }
107 })
108 }
109
110 func TestCgroupGOMAXPROCSRound(t *testing.T) {
111 mustHaveFourCPUs(t)
112
113 exe, err := buildTestProg(t, "testprog")
114 if err != nil {
115 t.Fatal(err)
116 }
117
118 tests := []struct {
119 quota int64
120 want int
121 }{
122
123 {
124 quota: 200001,
125 want: 3,
126 },
127 {
128 quota: 250000,
129 want: 3,
130 },
131 {
132 quota: 299999,
133 want: 3,
134 },
135
136 {
137 quota: 50000,
138 want: 2,
139 },
140 {
141 quota: 100000,
142 want: 2,
143 },
144 {
145 quota: 150000,
146 want: 2,
147 },
148 }
149 for _, tc := range tests {
150 t.Run(fmt.Sprintf("%d", tc.quota), func(t *testing.T) {
151 cgrouptest.InCgroupV2(t, func(c *cgrouptest.CgroupV2) {
152 if err := c.SetCPUMax(tc.quota, 100000); err != nil {
153 t.Fatalf("unable to set CPU limit: %v", err)
154 }
155
156 got := runBuiltTestProg(t, exe, "PrintGOMAXPROCS")
157 want := fmt.Sprintf("%d\n", tc.want)
158 if got != want {
159 t.Fatalf("output got %q want %q", got, want)
160 }
161 })
162 })
163 }
164 }
165
166
167 func TestCgroupGOMAXPROCSEnvironment(t *testing.T) {
168 mustHaveFourCPUs(t)
169
170 exe, err := buildTestProg(t, "testprog")
171 if err != nil {
172 t.Fatal(err)
173 }
174
175 cgrouptest.InCgroupV2(t, func(c *cgrouptest.CgroupV2) {
176 if err := c.SetCPUMax(200000, 100000); err != nil {
177 t.Fatalf("unable to set CPU limit: %v", err)
178 }
179
180 got := runBuiltTestProg(t, exe, "PrintGOMAXPROCS", "GOMAXPROCS=3")
181 want := "3\n"
182 if got != want {
183 t.Fatalf("output got %q want %q", got, want)
184 }
185 })
186 }
187
188
189 func TestCgroupGOMAXPROCSSchedAffinity(t *testing.T) {
190 exe, err := buildTestProg(t, "testprog")
191 if err != nil {
192 t.Fatal(err)
193 }
194
195 cgrouptest.InCgroupV2(t, func(c *cgrouptest.CgroupV2) {
196 if err := c.SetCPUMax(300000, 100000); err != nil {
197 t.Fatalf("unable to set CPU limit: %v", err)
198 }
199
200
201 runtime.LockOSThread()
202 defer runtime.UnlockOSThread()
203
204 const maxCPUs = 64 * 1024
205 var orig [maxCPUs / 8]byte
206 _, _, errno := syscall.Syscall6(syscall.SYS_SCHED_GETAFFINITY, 0, unsafe.Sizeof(orig), uintptr(unsafe.Pointer(&orig[0])), 0, 0, 0)
207 if errno != 0 {
208 t.Fatalf("unable to get CPU affinity: %v", errno)
209 }
210
211
212 if orig[0]&0b11 != 0b11 {
213 t.Skipf("skipping test: CPUs 0 and 1 not available")
214 }
215
216 var mask [maxCPUs / 8]byte
217 mask[0] = 0b11
218 _, _, errno = syscall.Syscall6(syscall.SYS_SCHED_SETAFFINITY, 0, unsafe.Sizeof(mask), uintptr(unsafe.Pointer(&mask[0])), 0, 0, 0)
219 if errno != 0 {
220 t.Fatalf("unable to set CPU affinity: %v", errno)
221 }
222 defer func() {
223 _, _, errno = syscall.Syscall6(syscall.SYS_SCHED_SETAFFINITY, 0, unsafe.Sizeof(orig), uintptr(unsafe.Pointer(&orig[0])), 0, 0, 0)
224 if errno != 0 {
225 t.Fatalf("unable to restore CPU affinity: %v", errno)
226 }
227 }()
228
229 got := runBuiltTestProg(t, exe, "PrintGOMAXPROCS")
230 want := "2\n"
231 if got != want {
232 t.Fatalf("output got %q want %q", got, want)
233 }
234 })
235 }
236
237 func TestCgroupGOMAXPROCSSetDefault(t *testing.T) {
238 mustHaveFourCPUs(t)
239
240 exe, err := buildTestProg(t, "testprog")
241 if err != nil {
242 t.Fatal(err)
243 }
244
245 tests := []struct {
246 godebug int
247 want int
248 }{
249
250
251 {
252 godebug: 1,
253 want: 3,
254 },
255
256 {
257 godebug: 0,
258 want: runtime.NumCPU(),
259 },
260 }
261 for _, tc := range tests {
262 t.Run(fmt.Sprintf("containermaxprocs=%d", tc.godebug), func(t *testing.T) {
263 cgrouptest.InCgroupV2(t, func(c *cgrouptest.CgroupV2) {
264 env := []string{
265 fmt.Sprintf("GO_TEST_CPU_MAX_PATH=%s", c.CPUMaxPath()),
266 "GO_TEST_CPU_MAX_QUOTA=300000",
267 fmt.Sprintf("GODEBUG=containermaxprocs=%d", tc.godebug),
268 }
269 got := runBuiltTestProg(t, exe, "SetLimitThenDefaultGOMAXPROCS", env...)
270 want := fmt.Sprintf("%d\n", tc.want)
271 if got != want {
272 t.Fatalf("output got %q want %q", got, want)
273 }
274 })
275 })
276 }
277 }
278
279 func TestCgroupGOMAXPROCSUpdate(t *testing.T) {
280 mustHaveFourCPUs(t)
281
282 if testing.Short() {
283 t.Skip("skipping test: long sleeps")
284 }
285
286 exe, err := buildTestProg(t, "testprog")
287 if err != nil {
288 t.Fatal(err)
289 }
290
291 cgrouptest.InCgroupV2(t, func(c *cgrouptest.CgroupV2) {
292 got := runBuiltTestProg(t, exe, "UpdateGOMAXPROCS", fmt.Sprintf("GO_TEST_CPU_MAX_PATH=%s", c.CPUMaxPath()))
293 if !strings.Contains(got, "OK") {
294 t.Fatalf("output got %q want OK", got)
295 }
296 })
297 }
298
299 func TestCgroupGOMAXPROCSDontUpdate(t *testing.T) {
300 mustHaveFourCPUs(t)
301
302 if testing.Short() {
303 t.Skip("skipping test: long sleeps")
304 }
305
306 exe, err := buildTestProg(t, "testprog")
307 if err != nil {
308 t.Fatal(err)
309 }
310
311
312
313 for _, v := range []string{"GOMAXPROCS=4", "GODEBUG=updatemaxprocs=0"} {
314 t.Run(v, func(t *testing.T) {
315 cgrouptest.InCgroupV2(t, func(c *cgrouptest.CgroupV2) {
316 got := runBuiltTestProg(t, exe, "DontUpdateGOMAXPROCS",
317 fmt.Sprintf("GO_TEST_CPU_MAX_PATH=%s", c.CPUMaxPath()),
318 v)
319 if !strings.Contains(got, "OK") {
320 t.Fatalf("output got %q want OK", got)
321 }
322 })
323 })
324 }
325 }
326
View as plain text