// +build linux package mem import ( "context" "encoding/json" "math" "os" "strconv" "strings" "github.com/shirou/gopsutil/internal/common" "golang.org/x/sys/unix" ) type VirtualMemoryExStat struct { ActiveFile uint64 `json:"activefile"` InactiveFile uint64 `json:"inactivefile"` ActiveAnon uint64 `json:"activeanon"` InactiveAnon uint64 `json:"inactiveanon"` Unevictable uint64 `json:"unevictable"` } func (v VirtualMemoryExStat) String() string { s, _ := json.Marshal(v) return string(s) } func VirtualMemory() (*VirtualMemoryStat, error) { return VirtualMemoryWithContext(context.Background()) } func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { vm, _, err := fillFromMeminfoWithContext(ctx) if err != nil { return nil, err } return vm, nil } func VirtualMemoryEx() (*VirtualMemoryExStat, error) { return VirtualMemoryExWithContext(context.Background()) } func VirtualMemoryExWithContext(ctx context.Context) (*VirtualMemoryExStat, error) { _, vmEx, err := fillFromMeminfoWithContext(ctx) if err != nil { return nil, err } return vmEx, nil } func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *VirtualMemoryExStat, error) { filename := common.HostProc("meminfo") lines, _ := common.ReadLines(filename) // flag if MemAvailable is in /proc/meminfo (kernel 3.14+) memavail := false activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008 inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008 sReclaimable := false // "SReclaimable:" not available: 2.6.19 / Nov 2006 ret := &VirtualMemoryStat{} retEx := &VirtualMemoryExStat{} for _, line := range lines { fields := strings.Split(line, ":") if len(fields) != 2 { continue } key := strings.TrimSpace(fields[0]) value := strings.TrimSpace(fields[1]) value = strings.Replace(value, " kB", "", -1) t, err := strconv.ParseUint(value, 10, 64) if err != nil { return ret, retEx,err } switch key { case "MemTotal": ret.Total = t * 1024 case "MemFree": ret.Free = t * 1024 case "MemAvailable": memavail = true ret.Available = t * 1024 case "Buffers": ret.Buffers = t * 1024 case "Cached": ret.Cached = t * 1024 case "Active": ret.Active = t * 1024 case "Inactive": ret.Inactive = t * 1024 case "Active(anon)": retEx.ActiveAnon = t * 1024 case "Inactive(anon)": retEx.InactiveAnon = t * 1024 case "Active(file)": activeFile = true retEx.ActiveFile = t * 1024 case "Inactive(file)": inactiveFile = true retEx.InactiveFile = t * 1024 case "Unevictable": retEx.Unevictable = t * 1024 case "Writeback": ret.Writeback = t * 1024 case "WritebackTmp": ret.WritebackTmp = t * 1024 case "Dirty": ret.Dirty = t * 1024 case "Shmem": ret.Shared = t * 1024 case "Slab": ret.Slab = t * 1024 case "SReclaimable": sReclaimable = true ret.SReclaimable = t * 1024 case "SUnreclaim": ret.SUnreclaim = t * 1024 case "PageTables": ret.PageTables = t * 1024 case "SwapCached": ret.SwapCached = t * 1024 case "CommitLimit": ret.CommitLimit = t * 1024 case "Committed_AS": ret.CommittedAS = t * 1024 case "HighTotal": ret.HighTotal = t * 1024 case "HighFree": ret.HighFree = t * 1024 case "LowTotal": ret.LowTotal = t * 1024 case "LowFree": ret.LowFree = t * 1024 case "SwapTotal": ret.SwapTotal = t * 1024 case "SwapFree": ret.SwapFree = t * 1024 case "Mapped": ret.Mapped = t * 1024 case "VmallocTotal": ret.VMallocTotal = t * 1024 case "VmallocUsed": ret.VMallocUsed = t * 1024 case "VmallocChunk": ret.VMallocChunk = t * 1024 case "HugePages_Total": ret.HugePagesTotal = t case "HugePages_Free": ret.HugePagesFree = t case "Hugepagesize": ret.HugePageSize = t * 1024 } } ret.Cached += ret.SReclaimable if !memavail { if activeFile && inactiveFile && sReclaimable { ret.Available = calcuateAvailVmem(ret, retEx) } else { ret.Available = ret.Cached + ret.Free } } ret.Used = ret.Total - ret.Free - ret.Buffers - ret.Cached ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 return ret, retEx, nil } func SwapMemory() (*SwapMemoryStat, error) { return SwapMemoryWithContext(context.Background()) } func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { sysinfo := &unix.Sysinfo_t{} if err := unix.Sysinfo(sysinfo); err != nil { return nil, err } ret := &SwapMemoryStat{ Total: uint64(sysinfo.Totalswap) * uint64(sysinfo.Unit), Free: uint64(sysinfo.Freeswap) * uint64(sysinfo.Unit), } ret.Used = ret.Total - ret.Free //check Infinity if ret.Total != 0 { ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0 } else { ret.UsedPercent = 0 } filename := common.HostProc("vmstat") lines, _ := common.ReadLines(filename) for _, l := range lines { fields := strings.Fields(l) if len(fields) < 2 { continue } switch fields[0] { case "pswpin": value, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } ret.Sin = value * 4 * 1024 case "pswpout": value, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } ret.Sout = value * 4 * 1024 case "pgpgin": value, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } ret.PgIn = value * 4 * 1024 case "pgpgout": value, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } ret.PgOut = value * 4 * 1024 case "pgfault": value, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } ret.PgFault = value * 4 * 1024 case "pgmajfault": value, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { continue } ret.PgMajFault = value * 4 * 1024 } } return ret, nil } // calcuateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide // "MemAvailable:" column. It reimplements an algorithm from the link below // https://github.com/giampaolo/psutil/pull/890 func calcuateAvailVmem(ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint64 { var watermarkLow uint64 fn := common.HostProc("zoneinfo") lines, err := common.ReadLines(fn) if err != nil { return ret.Free + ret.Cached // fallback under kernel 2.6.13 } pagesize := uint64(os.Getpagesize()) watermarkLow = 0 for _, line := range lines { fields := strings.Fields(line) if strings.HasPrefix(fields[0], "low") { lowValue, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { lowValue = 0 } watermarkLow += lowValue } } watermarkLow *= pagesize availMemory := ret.Free - watermarkLow pageCache := retEx.ActiveFile + retEx.InactiveFile pageCache -= uint64(math.Min(float64(pageCache/2), float64(watermarkLow))) availMemory += pageCache availMemory += ret.SReclaimable - uint64(math.Min(float64(ret.SReclaimable/2.0), float64(watermarkLow))) if availMemory < 0 { availMemory = 0 } return availMemory }