...

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

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

     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  	// Some custom kernels has extra fields that should be ignored
    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 // 300M
    46  		memoryAfter       = 524288000 // 500M
    47  		reservationBefore = 209715200 // 200M
    48  		reservationAfter  = 314572800 // 300M
    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 // 300M
    87  		memoryswapAfter  = 524288000 // 500M
    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 // 300M
   116  		memoryswapBefore = 524288000 // 500M
   117  		memoryAfter      = 629145600 // 600M
   118  		memoryswapAfter  = 838860800 // 800M
   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  		// Set will call getMemoryData when memory and swap memory are
   125  		// both set, fake these fields so we don't get error.
   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 // 600M
   162  		memoryswapBefore = 838860800 // 800M
   163  		memoryAfter      = 314572800 // 300M
   164  		memoryswapAfter  = 524288000 // 500M
   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 // default is 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 // disable oom killer, default is 0
   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