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 }