github.com/rudderlabs/rudder-go-kit@v0.30.0/mem/internal/cgroup/mem.go (about)

     1  package cgroup
     2  
     3  import (
     4  	"strconv"
     5  )
     6  
     7  // GetMemoryUsage returns cgroup (v1 or v2) memory usage
     8  func GetMemoryUsage(basePath string) int64 {
     9  	n, err := getMemStatCgroup1(basePath, "memory.usage_in_bytes")
    10  	if err == nil {
    11  		wss := getWSSMemoryCgroup1(basePath, n)
    12  		rss := getRSSMemoryCgroup1(basePath)
    13  		if wss > rss {
    14  			return wss
    15  		}
    16  		return rss
    17  	}
    18  	n, err = getMemStatCgroup2(basePath, "memory.current")
    19  	if err != nil {
    20  		return 0
    21  	}
    22  	return getWSSMemoryCgroup2(basePath, n)
    23  }
    24  
    25  // GetMemoryLimit returns the cgroup's (v1 or v2) memory limit, or [totalMem] if there is no limit set.
    26  // If using cgroups v1, hierarchical memory limit is also taken into consideration if there is no limit set.
    27  //
    28  // - https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
    29  //
    30  // - https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files
    31  func GetMemoryLimit(basePath string, totalMem int) int {
    32  	getLimit := func() int64 {
    33  		// cgroups v1
    34  		n, err := getMemStatCgroup1(basePath, "memory.limit_in_bytes")
    35  		if err == nil {
    36  			if n <= 0 || int64(int(n)) != n || int(n) > totalMem {
    37  				// try to get hierarchical limit
    38  				n = GetHierarchicalMemoryLimitCgroup1(basePath)
    39  			}
    40  			return n
    41  		}
    42  
    43  		// cgroups v2
    44  		n, err = getMemStatCgroup2(basePath, "memory.max")
    45  		if err != nil {
    46  			return 0
    47  		}
    48  		return n
    49  	}
    50  	limit := getLimit()
    51  
    52  	// if the number in not within expected boundaries, return totalMem
    53  	if limit <= 0 || int64(int(limit)) != limit || int(limit) > totalMem {
    54  		return totalMem
    55  	}
    56  	return int(limit)
    57  }
    58  
    59  func getMemStatCgroup2(basePath, statName string) (int64, error) {
    60  	// See https: //www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files
    61  	return getStatGeneric(statName, basePath+"/sys/fs/cgroup", basePath+"/proc/self/cgroup", "")
    62  }
    63  
    64  func getMemStatCgroup1(basePath, statName string) (int64, error) {
    65  	return getStatGeneric(statName, basePath+"/sys/fs/cgroup/memory", basePath+"/proc/self/cgroup", "memory")
    66  }
    67  
    68  // GetHierarchicalMemoryLimitCgroup1 returns hierarchical memory limit
    69  // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
    70  func GetHierarchicalMemoryLimitCgroup1(basePath string) int64 {
    71  	return memStatCgroup1(basePath, "hierarchical_memory_limit")
    72  }
    73  
    74  func getRSSMemoryCgroup1(basePath string) int64 {
    75  	return memStatCgroup1(basePath, "total_rss")
    76  }
    77  
    78  func getWSSMemoryCgroup1(basePath string, used int64) int64 {
    79  	inactive := memStatCgroup1(basePath, "total_inactive_file")
    80  	if used < inactive {
    81  		return 0
    82  	}
    83  	return used - inactive
    84  }
    85  
    86  func getWSSMemoryCgroup2(basePath string, used int64) int64 {
    87  	inactive := memStatCgroup2(basePath, "inactive_file")
    88  	if used < inactive {
    89  		return 0
    90  	}
    91  	return used - inactive
    92  }
    93  
    94  func memStatCgroup1(basePath, key string) int64 {
    95  	data, err := getFileContents("memory.stat", basePath+"/sys/fs/cgroup/memory", basePath+"/proc/self/cgroup", "memory")
    96  	if err != nil {
    97  		return 0
    98  	}
    99  	memStat, err := grepFirstMatch(data, key, 1, " ")
   100  	if err != nil {
   101  		return 0
   102  	}
   103  	n, err := strconv.ParseInt(memStat, 10, 64)
   104  	if err != nil {
   105  		return 0
   106  	}
   107  	return n
   108  }
   109  
   110  func memStatCgroup2(basePath, key string) int64 {
   111  	data, err := getFileContents("memory.stat", basePath+"/sys/fs/cgroup", basePath+"/proc/self/cgroup", "")
   112  	if err != nil {
   113  		return 0
   114  	}
   115  	memStat, err := grepFirstMatch(data, key, 1, " ")
   116  	if err != nil {
   117  		return 0
   118  	}
   119  	n, err := strconv.ParseInt(memStat, 10, 64)
   120  	if err != nil {
   121  		return 0
   122  	}
   123  	return n
   124  }