mirror of
https://github.com/status-im/consul.git
synced 2025-02-15 07:07:18 +00:00
https://github.com/shirou/gopsutil/pull/895 is merged and fixes our problem. Time to update. Since there is no new version just yet, updating to the sha.
283 lines
6.8 KiB
Go
283 lines
6.8 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
|
|
}
|
|
}
|
|
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
|
|
}
|