github.com/boomhut/fiber/v2@v2.0.0-20230603160335-b65c856e57d3/internal/gopsutil/mem/mem_linux.go (about) 1 //go:build linux 2 // +build linux 3 4 package mem 5 6 import ( 7 "context" 8 "encoding/json" 9 "math" 10 "os" 11 "strconv" 12 "strings" 13 14 "github.com/boomhut/fiber/v2/internal/gopsutil/common" 15 "golang.org/x/sys/unix" 16 ) 17 18 type VirtualMemoryExStat struct { 19 ActiveFile uint64 `json:"activefile"` 20 InactiveFile uint64 `json:"inactivefile"` 21 ActiveAnon uint64 `json:"activeanon"` 22 InactiveAnon uint64 `json:"inactiveanon"` 23 Unevictable uint64 `json:"unevictable"` 24 } 25 26 func (v VirtualMemoryExStat) String() string { 27 s, _ := json.Marshal(v) 28 return string(s) 29 } 30 31 func VirtualMemory() (*VirtualMemoryStat, error) { 32 return VirtualMemoryWithContext(context.Background()) 33 } 34 35 func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { 36 vm, _, err := fillFromMeminfoWithContext(ctx) 37 if err != nil { 38 return nil, err 39 } 40 return vm, nil 41 } 42 43 func VirtualMemoryEx() (*VirtualMemoryExStat, error) { 44 return VirtualMemoryExWithContext(context.Background()) 45 } 46 47 func VirtualMemoryExWithContext(ctx context.Context) (*VirtualMemoryExStat, error) { 48 _, vmEx, err := fillFromMeminfoWithContext(ctx) 49 if err != nil { 50 return nil, err 51 } 52 return vmEx, nil 53 } 54 55 func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *VirtualMemoryExStat, error) { 56 filename := common.HostProc("meminfo") 57 lines, _ := common.ReadLines(filename) 58 59 // flag if MemAvailable is in /proc/meminfo (kernel 3.14+) 60 memavail := false 61 activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008 62 inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008 63 sReclaimable := false // "SReclaimable:" not available: 2.6.19 / Nov 2006 64 65 ret := &VirtualMemoryStat{} 66 retEx := &VirtualMemoryExStat{} 67 68 for _, line := range lines { 69 fields := strings.Split(line, ":") 70 if len(fields) != 2 { 71 continue 72 } 73 key := strings.TrimSpace(fields[0]) 74 value := strings.TrimSpace(fields[1]) 75 value = strings.Replace(value, " kB", "", -1) 76 77 t, err := strconv.ParseUint(value, 10, 64) 78 if err != nil { 79 return ret, retEx, err 80 } 81 switch key { 82 case "MemTotal": 83 ret.Total = t * 1024 84 case "MemFree": 85 ret.Free = t * 1024 86 case "MemAvailable": 87 memavail = true 88 ret.Available = t * 1024 89 case "Buffers": 90 ret.Buffers = t * 1024 91 case "Cached": 92 ret.Cached = t * 1024 93 case "Active": 94 ret.Active = t * 1024 95 case "Inactive": 96 ret.Inactive = t * 1024 97 case "Active(anon)": 98 retEx.ActiveAnon = t * 1024 99 case "Inactive(anon)": 100 retEx.InactiveAnon = t * 1024 101 case "Active(file)": 102 activeFile = true 103 retEx.ActiveFile = t * 1024 104 case "Inactive(file)": 105 inactiveFile = true 106 retEx.InactiveFile = t * 1024 107 case "Unevictable": 108 retEx.Unevictable = t * 1024 109 case "Writeback": 110 ret.Writeback = t * 1024 111 case "WritebackTmp": 112 ret.WritebackTmp = t * 1024 113 case "Dirty": 114 ret.Dirty = t * 1024 115 case "Shmem": 116 ret.Shared = t * 1024 117 case "Slab": 118 ret.Slab = t * 1024 119 case "SReclaimable": 120 sReclaimable = true 121 ret.SReclaimable = t * 1024 122 case "SUnreclaim": 123 ret.SUnreclaim = t * 1024 124 case "PageTables": 125 ret.PageTables = t * 1024 126 case "SwapCached": 127 ret.SwapCached = t * 1024 128 case "CommitLimit": 129 ret.CommitLimit = t * 1024 130 case "Committed_AS": 131 ret.CommittedAS = t * 1024 132 case "HighTotal": 133 ret.HighTotal = t * 1024 134 case "HighFree": 135 ret.HighFree = t * 1024 136 case "LowTotal": 137 ret.LowTotal = t * 1024 138 case "LowFree": 139 ret.LowFree = t * 1024 140 case "SwapTotal": 141 ret.SwapTotal = t * 1024 142 case "SwapFree": 143 ret.SwapFree = t * 1024 144 case "Mapped": 145 ret.Mapped = t * 1024 146 case "VmallocTotal": 147 ret.VMallocTotal = t * 1024 148 case "VmallocUsed": 149 ret.VMallocUsed = t * 1024 150 case "VmallocChunk": 151 ret.VMallocChunk = t * 1024 152 case "HugePages_Total": 153 ret.HugePagesTotal = t 154 case "HugePages_Free": 155 ret.HugePagesFree = t 156 case "Hugepagesize": 157 ret.HugePageSize = t * 1024 158 } 159 } 160 161 ret.Cached += ret.SReclaimable 162 163 if !memavail { 164 if activeFile && inactiveFile && sReclaimable { 165 ret.Available = calcuateAvailVmem(ret, retEx) 166 } else { 167 ret.Available = ret.Cached + ret.Free 168 } 169 } 170 171 ret.Used = ret.Total - ret.Free - ret.Buffers - ret.Cached 172 ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 173 174 return ret, retEx, nil 175 } 176 177 func SwapMemory() (*SwapMemoryStat, error) { 178 return SwapMemoryWithContext(context.Background()) 179 } 180 181 func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { 182 sysinfo := &unix.Sysinfo_t{} 183 184 if err := unix.Sysinfo(sysinfo); err != nil { 185 return nil, err 186 } 187 ret := &SwapMemoryStat{ 188 Total: uint64(sysinfo.Totalswap) * uint64(sysinfo.Unit), 189 Free: uint64(sysinfo.Freeswap) * uint64(sysinfo.Unit), 190 } 191 ret.Used = ret.Total - ret.Free 192 //check Infinity 193 if ret.Total != 0 { 194 ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0 195 } else { 196 ret.UsedPercent = 0 197 } 198 filename := common.HostProc("vmstat") 199 lines, _ := common.ReadLines(filename) 200 for _, l := range lines { 201 fields := strings.Fields(l) 202 if len(fields) < 2 { 203 continue 204 } 205 switch fields[0] { 206 case "pswpin": 207 value, err := strconv.ParseUint(fields[1], 10, 64) 208 if err != nil { 209 continue 210 } 211 ret.Sin = value * 4 * 1024 212 case "pswpout": 213 value, err := strconv.ParseUint(fields[1], 10, 64) 214 if err != nil { 215 continue 216 } 217 ret.Sout = value * 4 * 1024 218 case "pgpgin": 219 value, err := strconv.ParseUint(fields[1], 10, 64) 220 if err != nil { 221 continue 222 } 223 ret.PgIn = value * 4 * 1024 224 case "pgpgout": 225 value, err := strconv.ParseUint(fields[1], 10, 64) 226 if err != nil { 227 continue 228 } 229 ret.PgOut = value * 4 * 1024 230 case "pgfault": 231 value, err := strconv.ParseUint(fields[1], 10, 64) 232 if err != nil { 233 continue 234 } 235 ret.PgFault = value * 4 * 1024 236 case "pgmajfault": 237 value, err := strconv.ParseUint(fields[1], 10, 64) 238 if err != nil { 239 continue 240 } 241 ret.PgMajFault = value * 4 * 1024 242 } 243 } 244 return ret, nil 245 } 246 247 // calcuateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide 248 // "MemAvailable:" column. It reimplements an algorithm from the link below 249 // https://github.com/giampaolo/psutil/pull/890 250 func calcuateAvailVmem(ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint64 { 251 var watermarkLow uint64 252 253 fn := common.HostProc("zoneinfo") 254 lines, err := common.ReadLines(fn) 255 256 if err != nil { 257 return ret.Free + ret.Cached // fallback under kernel 2.6.13 258 } 259 260 pagesize := uint64(os.Getpagesize()) 261 watermarkLow = 0 262 263 for _, line := range lines { 264 fields := strings.Fields(line) 265 266 if strings.HasPrefix(fields[0], "low") { 267 lowValue, err := strconv.ParseUint(fields[1], 10, 64) 268 269 if err != nil { 270 lowValue = 0 271 } 272 watermarkLow += lowValue 273 } 274 } 275 276 watermarkLow *= pagesize 277 278 availMemory := ret.Free - watermarkLow 279 pageCache := retEx.ActiveFile + retEx.InactiveFile 280 pageCache -= uint64(math.Min(float64(pageCache/2), float64(watermarkLow))) 281 availMemory += pageCache 282 availMemory += ret.SReclaimable - uint64(math.Min(float64(ret.SReclaimable/2.0), float64(watermarkLow))) 283 284 return availMemory 285 }