shirou_gopsutil/mem/mem_linux.go

289 lines
6.9 KiB
Go

// +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
}