1 package fs
2
3 import (
4 "strconv"
5 "testing"
6
7 "github.com/opencontainers/runc/libcontainer/cgroups"
8 "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
9 "github.com/opencontainers/runc/libcontainer/configs"
10 )
11
12 const (
13 memoryStatContents = `cache 512
14 rss 1024`
15 memoryUsageContents = "2048\n"
16 memoryMaxUsageContents = "4096\n"
17 memoryFailcnt = "100\n"
18 memoryLimitContents = "8192\n"
19 memoryUseHierarchyContents = "1\n"
20 memoryNUMAStatContents = `total=44611 N0=32631 N1=7501 N2=1982 N3=2497
21 file=44428 N0=32614 N1=7335 N2=1982 N3=2497
22 anon=183 N0=17 N1=166 N2=0 N3=0
23 unevictable=0 N0=0 N1=0 N2=0 N3=0
24 hierarchical_total=768133 N0=509113 N1=138887 N2=20464 N3=99669
25 hierarchical_file=722017 N0=496516 N1=119997 N2=20181 N3=85323
26 hierarchical_anon=46096 N0=12597 N1=18890 N2=283 N3=14326
27 hierarchical_unevictable=20 N0=0 N1=0 N2=0 N3=20
28 `
29 memoryNUMAStatNoHierarchyContents = `total=44611 N0=32631 N1=7501 N2=1982 N3=2497
30 file=44428 N0=32614 N1=7335 N2=1982 N3=2497
31 anon=183 N0=17 N1=166 N2=0 N3=0
32 unevictable=0 N0=0 N1=0 N2=0 N3=0
33 `
34
35 memoryNUMAStatExtraContents = `numa_locality 0 0 0 0 0 0 0 0 0 0
36 numa_exectime 0
37 whatever=100 N0=0
38 `
39 )
40
41 func TestMemorySetMemory(t *testing.T) {
42 path := tempDir(t, "memory")
43
44 const (
45 memoryBefore = 314572800
46 memoryAfter = 524288000
47 reservationBefore = 209715200
48 reservationAfter = 314572800
49 )
50
51 writeFileContents(t, path, map[string]string{
52 "memory.limit_in_bytes": strconv.Itoa(memoryBefore),
53 "memory.soft_limit_in_bytes": strconv.Itoa(reservationBefore),
54 })
55
56 r := &configs.Resources{
57 Memory: memoryAfter,
58 MemoryReservation: reservationAfter,
59 }
60 memory := &MemoryGroup{}
61 if err := memory.Set(path, r); err != nil {
62 t.Fatal(err)
63 }
64
65 value, err := fscommon.GetCgroupParamUint(path, "memory.limit_in_bytes")
66 if err != nil {
67 t.Fatal(err)
68 }
69 if value != memoryAfter {
70 t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.")
71 }
72
73 value, err = fscommon.GetCgroupParamUint(path, "memory.soft_limit_in_bytes")
74 if err != nil {
75 t.Fatal(err)
76 }
77 if value != reservationAfter {
78 t.Fatal("Got the wrong value, set memory.soft_limit_in_bytes failed.")
79 }
80 }
81
82 func TestMemorySetMemoryswap(t *testing.T) {
83 path := tempDir(t, "memory")
84
85 const (
86 memoryswapBefore = 314572800
87 memoryswapAfter = 524288000
88 )
89
90 writeFileContents(t, path, map[string]string{
91 "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore),
92 })
93
94 r := &configs.Resources{
95 MemorySwap: memoryswapAfter,
96 }
97 memory := &MemoryGroup{}
98 if err := memory.Set(path, r); err != nil {
99 t.Fatal(err)
100 }
101
102 value, err := fscommon.GetCgroupParamUint(path, "memory.memsw.limit_in_bytes")
103 if err != nil {
104 t.Fatal(err)
105 }
106 if value != memoryswapAfter {
107 t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.")
108 }
109 }
110
111 func TestMemorySetMemoryLargerThanSwap(t *testing.T) {
112 path := tempDir(t, "memory")
113
114 const (
115 memoryBefore = 314572800
116 memoryswapBefore = 524288000
117 memoryAfter = 629145600
118 memoryswapAfter = 838860800
119 )
120
121 writeFileContents(t, path, map[string]string{
122 "memory.limit_in_bytes": strconv.Itoa(memoryBefore),
123 "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore),
124
125
126 "memory.usage_in_bytes": "0",
127 "memory.max_usage_in_bytes": "0",
128 "memory.failcnt": "0",
129 })
130
131 r := &configs.Resources{
132 Memory: memoryAfter,
133 MemorySwap: memoryswapAfter,
134 }
135 memory := &MemoryGroup{}
136 if err := memory.Set(path, r); err != nil {
137 t.Fatal(err)
138 }
139
140 value, err := fscommon.GetCgroupParamUint(path, "memory.limit_in_bytes")
141 if err != nil {
142 t.Fatal(err)
143 }
144 if value != memoryAfter {
145 t.Fatal("Got the wrong value, set memory.limit_in_bytes failed.")
146 }
147
148 value, err = fscommon.GetCgroupParamUint(path, "memory.memsw.limit_in_bytes")
149 if err != nil {
150 t.Fatal(err)
151 }
152 if value != memoryswapAfter {
153 t.Fatal("Got the wrong value, set memory.memsw.limit_in_bytes failed.")
154 }
155 }
156
157 func TestMemorySetSwapSmallerThanMemory(t *testing.T) {
158 path := tempDir(t, "memory")
159
160 const (
161 memoryBefore = 629145600
162 memoryswapBefore = 838860800
163 memoryAfter = 314572800
164 memoryswapAfter = 524288000
165 )
166
167 writeFileContents(t, path, map[string]string{
168 "memory.limit_in_bytes": strconv.Itoa(memoryBefore),
169 "memory.memsw.limit_in_bytes": strconv.Itoa(memoryswapBefore),
170 })
171
172 r := &configs.Resources{
173 Memory: memoryAfter,
174 MemorySwap: memoryswapAfter,
175 }
176 memory := &MemoryGroup{}
177 if err := memory.Set(path, r); err != nil {
178 t.Fatal(err)
179 }
180
181 value, err := fscommon.GetCgroupParamUint(path, "memory.limit_in_bytes")
182 if err != nil {
183 t.Fatal(err)
184 }
185 if value != memoryAfter {
186 t.Fatalf("Got the wrong value (%d != %d), set memory.limit_in_bytes failed", value, memoryAfter)
187 }
188
189 value, err = fscommon.GetCgroupParamUint(path, "memory.memsw.limit_in_bytes")
190 if err != nil {
191 t.Fatal(err)
192 }
193 if value != memoryswapAfter {
194 t.Fatalf("Got the wrong value (%d != %d), set memory.memsw.limit_in_bytes failed", value, memoryswapAfter)
195 }
196 }
197
198 func TestMemorySetMemorySwappinessDefault(t *testing.T) {
199 path := tempDir(t, "memory")
200
201 swappinessBefore := 60
202 swappinessAfter := uint64(0)
203
204 writeFileContents(t, path, map[string]string{
205 "memory.swappiness": strconv.Itoa(swappinessBefore),
206 })
207
208 r := &configs.Resources{
209 MemorySwappiness: &swappinessAfter,
210 }
211 memory := &MemoryGroup{}
212 if err := memory.Set(path, r); err != nil {
213 t.Fatal(err)
214 }
215
216 value, err := fscommon.GetCgroupParamUint(path, "memory.swappiness")
217 if err != nil {
218 t.Fatal(err)
219 }
220 if value != swappinessAfter {
221 t.Fatalf("Got the wrong value (%d), set memory.swappiness = %d failed.", value, swappinessAfter)
222 }
223 }
224
225 func TestMemoryStats(t *testing.T) {
226 path := tempDir(t, "memory")
227 writeFileContents(t, path, map[string]string{
228 "memory.stat": memoryStatContents,
229 "memory.usage_in_bytes": memoryUsageContents,
230 "memory.limit_in_bytes": memoryLimitContents,
231 "memory.max_usage_in_bytes": memoryMaxUsageContents,
232 "memory.failcnt": memoryFailcnt,
233 "memory.memsw.usage_in_bytes": memoryUsageContents,
234 "memory.memsw.max_usage_in_bytes": memoryMaxUsageContents,
235 "memory.memsw.failcnt": memoryFailcnt,
236 "memory.memsw.limit_in_bytes": memoryLimitContents,
237 "memory.kmem.usage_in_bytes": memoryUsageContents,
238 "memory.kmem.max_usage_in_bytes": memoryMaxUsageContents,
239 "memory.kmem.failcnt": memoryFailcnt,
240 "memory.kmem.limit_in_bytes": memoryLimitContents,
241 "memory.use_hierarchy": memoryUseHierarchyContents,
242 "memory.numa_stat": memoryNUMAStatContents + memoryNUMAStatExtraContents,
243 })
244
245 memory := &MemoryGroup{}
246 actualStats := *cgroups.NewStats()
247 err := memory.GetStats(path, &actualStats)
248 if err != nil {
249 t.Fatal(err)
250 }
251 expectedStats := cgroups.MemoryStats{
252 Cache: 512,
253 Usage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192},
254 SwapUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192},
255 SwapOnlyUsage: cgroups.MemoryData{Usage: 0, MaxUsage: 0, Failcnt: 0, Limit: 0},
256 KernelUsage: cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Limit: 8192},
257 Stats: map[string]uint64{"cache": 512, "rss": 1024},
258 UseHierarchy: true,
259 PageUsageByNUMA: cgroups.PageUsageByNUMA{
260 PageUsageByNUMAInner: cgroups.PageUsageByNUMAInner{
261 Total: cgroups.PageStats{Total: 44611, Nodes: map[uint8]uint64{0: 32631, 1: 7501, 2: 1982, 3: 2497}},
262 File: cgroups.PageStats{Total: 44428, Nodes: map[uint8]uint64{0: 32614, 1: 7335, 2: 1982, 3: 2497}},
263 Anon: cgroups.PageStats{Total: 183, Nodes: map[uint8]uint64{0: 17, 1: 166, 2: 0, 3: 0}},
264 Unevictable: cgroups.PageStats{Total: 0, Nodes: map[uint8]uint64{0: 0, 1: 0, 2: 0, 3: 0}},
265 },
266 Hierarchical: cgroups.PageUsageByNUMAInner{
267 Total: cgroups.PageStats{Total: 768133, Nodes: map[uint8]uint64{0: 509113, 1: 138887, 2: 20464, 3: 99669}},
268 File: cgroups.PageStats{Total: 722017, Nodes: map[uint8]uint64{0: 496516, 1: 119997, 2: 20181, 3: 85323}},
269 Anon: cgroups.PageStats{Total: 46096, Nodes: map[uint8]uint64{0: 12597, 1: 18890, 2: 283, 3: 14326}},
270 Unevictable: cgroups.PageStats{Total: 20, Nodes: map[uint8]uint64{0: 0, 1: 0, 2: 0, 3: 20}},
271 },
272 },
273 }
274 expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats)
275 }
276
277 func TestMemoryStatsNoStatFile(t *testing.T) {
278 path := tempDir(t, "memory")
279 writeFileContents(t, path, map[string]string{
280 "memory.usage_in_bytes": memoryUsageContents,
281 "memory.max_usage_in_bytes": memoryMaxUsageContents,
282 "memory.limit_in_bytes": memoryLimitContents,
283 })
284
285 memory := &MemoryGroup{}
286 actualStats := *cgroups.NewStats()
287 err := memory.GetStats(path, &actualStats)
288 if err != nil {
289 t.Fatal(err)
290 }
291 }
292
293 func TestMemoryStatsNoUsageFile(t *testing.T) {
294 path := tempDir(t, "memory")
295 writeFileContents(t, path, map[string]string{
296 "memory.stat": memoryStatContents,
297 "memory.max_usage_in_bytes": memoryMaxUsageContents,
298 "memory.limit_in_bytes": memoryLimitContents,
299 })
300
301 memory := &MemoryGroup{}
302 actualStats := *cgroups.NewStats()
303 err := memory.GetStats(path, &actualStats)
304 if err == nil {
305 t.Fatal("Expected failure")
306 }
307 }
308
309 func TestMemoryStatsNoMaxUsageFile(t *testing.T) {
310 path := tempDir(t, "memory")
311 writeFileContents(t, path, map[string]string{
312 "memory.stat": memoryStatContents,
313 "memory.usage_in_bytes": memoryUsageContents,
314 "memory.limit_in_bytes": memoryLimitContents,
315 })
316
317 memory := &MemoryGroup{}
318 actualStats := *cgroups.NewStats()
319 err := memory.GetStats(path, &actualStats)
320 if err == nil {
321 t.Fatal("Expected failure")
322 }
323 }
324
325 func TestMemoryStatsNoLimitInBytesFile(t *testing.T) {
326 path := tempDir(t, "memory")
327 writeFileContents(t, path, map[string]string{
328 "memory.stat": memoryStatContents,
329 "memory.usage_in_bytes": memoryUsageContents,
330 "memory.max_usage_in_bytes": memoryMaxUsageContents,
331 })
332
333 memory := &MemoryGroup{}
334 actualStats := *cgroups.NewStats()
335 err := memory.GetStats(path, &actualStats)
336 if err == nil {
337 t.Fatal("Expected failure")
338 }
339 }
340
341 func TestMemoryStatsBadStatFile(t *testing.T) {
342 path := tempDir(t, "memory")
343 writeFileContents(t, path, map[string]string{
344 "memory.stat": "rss rss",
345 "memory.usage_in_bytes": memoryUsageContents,
346 "memory.max_usage_in_bytes": memoryMaxUsageContents,
347 "memory.limit_in_bytes": memoryLimitContents,
348 })
349
350 memory := &MemoryGroup{}
351 actualStats := *cgroups.NewStats()
352 err := memory.GetStats(path, &actualStats)
353 if err == nil {
354 t.Fatal("Expected failure")
355 }
356 }
357
358 func TestMemoryStatsBadUsageFile(t *testing.T) {
359 path := tempDir(t, "memory")
360 writeFileContents(t, path, map[string]string{
361 "memory.stat": memoryStatContents,
362 "memory.usage_in_bytes": "bad",
363 "memory.max_usage_in_bytes": memoryMaxUsageContents,
364 "memory.limit_in_bytes": memoryLimitContents,
365 })
366
367 memory := &MemoryGroup{}
368 actualStats := *cgroups.NewStats()
369 err := memory.GetStats(path, &actualStats)
370 if err == nil {
371 t.Fatal("Expected failure")
372 }
373 }
374
375 func TestMemoryStatsBadMaxUsageFile(t *testing.T) {
376 path := tempDir(t, "memory")
377 writeFileContents(t, path, map[string]string{
378 "memory.stat": memoryStatContents,
379 "memory.usage_in_bytes": memoryUsageContents,
380 "memory.max_usage_in_bytes": "bad",
381 "memory.limit_in_bytes": memoryLimitContents,
382 })
383
384 memory := &MemoryGroup{}
385 actualStats := *cgroups.NewStats()
386 err := memory.GetStats(path, &actualStats)
387 if err == nil {
388 t.Fatal("Expected failure")
389 }
390 }
391
392 func TestMemoryStatsBadLimitInBytesFile(t *testing.T) {
393 path := tempDir(t, "memory")
394 writeFileContents(t, path, map[string]string{
395 "memory.stat": memoryStatContents,
396 "memory.usage_in_bytes": memoryUsageContents,
397 "memory.max_usage_in_bytes": memoryMaxUsageContents,
398 "memory.limit_in_bytes": "bad",
399 })
400
401 memory := &MemoryGroup{}
402 actualStats := *cgroups.NewStats()
403 err := memory.GetStats(path, &actualStats)
404 if err == nil {
405 t.Fatal("Expected failure")
406 }
407 }
408
409 func TestMemorySetOomControl(t *testing.T) {
410 path := tempDir(t, "memory")
411
412 const (
413 oomKillDisable = 1
414 )
415
416 writeFileContents(t, path, map[string]string{
417 "memory.oom_control": strconv.Itoa(oomKillDisable),
418 })
419
420 memory := &MemoryGroup{}
421 r := &configs.Resources{}
422 if err := memory.Set(path, r); err != nil {
423 t.Fatal(err)
424 }
425
426 value, err := fscommon.GetCgroupParamUint(path, "memory.oom_control")
427 if err != nil {
428 t.Fatal(err)
429 }
430 if value != oomKillDisable {
431 t.Fatalf("Got the wrong value, set memory.oom_control failed.")
432 }
433 }
434
435 func TestNoHierarchicalNumaStat(t *testing.T) {
436 path := tempDir(t, "memory")
437 writeFileContents(t, path, map[string]string{
438 "memory.numa_stat": memoryNUMAStatNoHierarchyContents + memoryNUMAStatExtraContents,
439 })
440
441 actualStats, err := getPageUsageByNUMA(path)
442 if err != nil {
443 t.Fatal(err)
444 }
445 pageUsageByNUMA := cgroups.PageUsageByNUMA{
446 PageUsageByNUMAInner: cgroups.PageUsageByNUMAInner{
447 Total: cgroups.PageStats{Total: 44611, Nodes: map[uint8]uint64{0: 32631, 1: 7501, 2: 1982, 3: 2497}},
448 File: cgroups.PageStats{Total: 44428, Nodes: map[uint8]uint64{0: 32614, 1: 7335, 2: 1982, 3: 2497}},
449 Anon: cgroups.PageStats{Total: 183, Nodes: map[uint8]uint64{0: 17, 1: 166, 2: 0, 3: 0}},
450 Unevictable: cgroups.PageStats{Total: 0, Nodes: map[uint8]uint64{0: 0, 1: 0, 2: 0, 3: 0}},
451 },
452 Hierarchical: cgroups.PageUsageByNUMAInner{},
453 }
454 expectPageUsageByNUMAEquals(t, pageUsageByNUMA, actualStats)
455 }
456
457 func TestBadNumaStat(t *testing.T) {
458 memoryNUMAStatBadContents := []struct {
459 desc, contents string
460 }{
461 {
462 desc: "Nx where x is not a number",
463 contents: `total=44611 N0=44611,
464 file=44428 Nx=0
465 `,
466 }, {
467 desc: "Nx where x > 255",
468 contents: `total=44611 N333=444`,
469 }, {
470 desc: "Nx argument missing",
471 contents: `total=44611 N0=123 N1=`,
472 }, {
473 desc: "Nx argument is not a number",
474 contents: `total=44611 N0=123 N1=a`,
475 }, {
476 desc: "Missing = after Nx",
477 contents: `total=44611 N0=123 N1`,
478 }, {
479 desc: "No Nx at non-first position",
480 contents: `total=44611 N0=32631
481 file=44428 N0=32614
482 anon=183 N0=12 badone
483 `,
484 },
485 }
486 path := tempDir(t, "memory")
487 for _, c := range memoryNUMAStatBadContents {
488 writeFileContents(t, path, map[string]string{
489 "memory.numa_stat": c.contents,
490 })
491
492 _, err := getPageUsageByNUMA(path)
493 if err == nil {
494 t.Errorf("case %q: expected error, got nil", c.desc)
495 }
496 }
497 }
498
499 func TestWithoutNumaStat(t *testing.T) {
500 path := tempDir(t, "memory")
501
502 actualStats, err := getPageUsageByNUMA(path)
503 if err != nil {
504 t.Fatal(err)
505 }
506 expectPageUsageByNUMAEquals(t, cgroups.PageUsageByNUMA{}, actualStats)
507 }
508
View as plain text