diff --git a/CHANGELOG.md b/CHANGELOG.md index 548b1ceee..e3b637924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). privileges in a process token. - Added utility code for interfacing with linux NETLINK_INET_DIAG. #60 - Added `ProcEnv` for getting a process's environment variables. #61 +- Read `MemAvailable` value for kernel 3.14+ ### Changed - Changed several `OpenProcess` calls on Windows to request the lowest possible diff --git a/sigar_linux_common.go b/sigar_linux_common.go index 7753a7e79..0a85e06f5 100644 --- a/sigar_linux_common.go +++ b/sigar_linux_common.go @@ -53,35 +53,38 @@ func (self *LoadAverage) Get() error { } func (self *Mem) Get() error { - var buffers, cached uint64 - table := map[string]*uint64{ - "MemTotal": &self.Total, - "MemFree": &self.Free, - "Buffers": &buffers, - "Cached": &cached, - } - if err := parseMeminfo(table); err != nil { + table, err := parseMeminfo() + if err != nil { return err } - self.Used = self.Total - self.Free - kern := buffers + cached - self.ActualFree = self.Free + kern - self.ActualUsed = self.Used - kern + self.Total, _ = table["MemTotal"] + self.Free, _ = table["MemFree"] + buffers, _ := table["Buffers"] + cached, _ := table["Cached"] + + if available, ok := table["MemAvailable"]; ok { + // MemAvailable is in /proc/meminfo (kernel 3.14+) + self.ActualFree = available + } else { + self.ActualFree = self.Free + buffers + cached + } + + self.Used = self.Total - self.ActualFree + self.ActualUsed = self.Used return nil } func (self *Swap) Get() error { - table := map[string]*uint64{ - "SwapTotal": &self.Total, - "SwapFree": &self.Free, - } - if err := parseMeminfo(table); err != nil { + table, err := parseMeminfo() + if err != nil { return err } + self.Total, _ = table["SwapTotal"] + self.Free, _ = table["SwapFree"] self.Used = self.Total - self.Free return nil @@ -353,20 +356,26 @@ func (self *ProcExe) Get(pid int) error { return nil } -func parseMeminfo(table map[string]*uint64) error { - return readFile(Procd+"/meminfo", func(line string) bool { +func parseMeminfo() (map[string]uint64, error) { + table := map[string]uint64{} + + err := readFile(Procd+"/meminfo", func(line string) bool { fields := strings.Split(line, ":") - if ptr := table[fields[0]]; ptr != nil { - num := strings.TrimLeft(fields[1], " ") - val, err := strtoull(strings.Fields(num)[0]) - if err == nil { - *ptr = val * 1024 - } + if len(fields) != 2 { + return true // skip on errors + } + + num := strings.TrimLeft(fields[1], " ") + val, err := strtoull(strings.Fields(num)[0]) + if err != nil { + return true // skip on errors } + table[fields[0]] = val * 1024 //in bytes return true }) + return table, err } func readFile(file string, handler func(string) bool) error { diff --git a/sigar_linux_test.go b/sigar_linux_test.go index a2a7fe33f..06990d875 100644 --- a/sigar_linux_test.go +++ b/sigar_linux_test.go @@ -218,6 +218,225 @@ DirectMap2M: 333824 kB } } +func TestLinuxMemAndSwapKernel_3_14(t *testing.T) { + setUp(t) + defer tearDown(t) + + meminfoContents := ` +MemTotal: 500184 kB +MemFree: 31360 kB +MemAvailable: 414168 kB +Buffers: 28740 kB +Cached: 325408 kB +SwapCached: 264 kB +Active: 195476 kB +Inactive: 198612 kB +Active(anon): 14920 kB +Inactive(anon): 27268 kB +Active(file): 180556 kB +Inactive(file): 171344 kB +Unevictable: 0 kB +Mlocked: 0 kB +SwapTotal: 524284 kB +SwapFree: 520352 kB +Dirty: 0 kB +Writeback: 0 kB +AnonPages: 39772 kB +Mapped: 24132 kB +Shmem: 2236 kB +Slab: 57988 kB +SReclaimable: 43524 kB +SUnreclaim: 14464 kB +KernelStack: 2464 kB +PageTables: 3096 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 774376 kB +Committed_AS: 490916 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 0 kB +VmallocChunk: 0 kB +HardwareCorrupted: 0 kB +AnonHugePages: 0 kB +CmaTotal: 0 kB +CmaFree: 0 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +DirectMap4k: 63424 kB +DirectMap2M: 460800 kB +` + + meminfoFile := procd + "/meminfo" + err := ioutil.WriteFile(meminfoFile, []byte(meminfoContents), 0444) + if err != nil { + t.Fatal(err) + } + + mem := sigar.Mem{} + if assert.NoError(t, mem.Get()) { + assert.Equal(t, uint64(500184*1024), mem.Total) + assert.Equal(t, uint64(31360*1024), mem.Free) + assert.Equal(t, uint64(414168*1024), mem.ActualFree) + } + + swap := sigar.Swap{} + if assert.NoError(t, swap.Get()) { + assert.Equal(t, uint64(524284*1024), swap.Total) + assert.Equal(t, uint64(520352*1024), swap.Free) + } +} + +func TestLinuxMemAndSwapMissingMemTotal(t *testing.T) { + setUp(t) + defer tearDown(t) + + meminfoContents := ` +MemFree: 31360 kB +MemAvailable: 414168 kB +Buffers: 28740 kB +Cached: 325408 kB +SwapCached: 264 kB +Active: 195476 kB +Inactive: 198612 kB +Active(anon): 14920 kB +Inactive(anon): 27268 kB +Active(file): 180556 kB +Inactive(file): 171344 kB +Unevictable: 0 kB +Mlocked: 0 kB +SwapTotal: 524284 kB +SwapFree: 520352 kB +Dirty: 0 kB +Writeback: 0 kB +AnonPages: 39772 kB +Mapped: 24132 kB +Shmem: 2236 kB +Slab: 57988 kB +SReclaimable: 43524 kB +SUnreclaim: 14464 kB +KernelStack: 2464 kB +PageTables: 3096 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 774376 kB +Committed_AS: 490916 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 0 kB +VmallocChunk: 0 kB +HardwareCorrupted: 0 kB +AnonHugePages: 0 kB +CmaTotal: 0 kB +CmaFree: 0 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +DirectMap4k: 63424 kB +DirectMap2M: 460800 kB +` + + meminfoFile := procd + "/meminfo" + err := ioutil.WriteFile(meminfoFile, []byte(meminfoContents), 0444) + if err != nil { + t.Fatal(err) + } + + mem := sigar.Mem{} + if assert.NoError(t, mem.Get()) { + assert.Equal(t, uint64(0), mem.Total) + assert.Equal(t, uint64(31360*1024), mem.Free) + assert.Equal(t, uint64(414168*1024), mem.ActualFree) + } + + swap := sigar.Swap{} + if assert.NoError(t, swap.Get()) { + assert.Equal(t, uint64(524284*1024), swap.Total) + assert.Equal(t, uint64(520352*1024), swap.Free) + } +} + +func TestLinuxMemAndSwapKernel_3_14_memavailable_zero(t *testing.T) { + setUp(t) + defer tearDown(t) + + meminfoContents := ` +MemTotal: 148535680 kB +MemFree: 417356 kB +MemAvailable: 0 kB +Buffers: 1728 kB +Cached: 129928 kB +SwapCached: 8208 kB +Active: 141088676 kB +Inactive: 5568132 kB +Active(anon): 141076780 kB +Inactive(anon): 5556936 kB +Active(file): 11896 kB +Inactive(file): 11196 kB +Unevictable: 3648 kB +Mlocked: 3648 kB +SwapTotal: 4882428 kB +SwapFree: 0 kB +Dirty: 808 kB +Writeback: 220 kB +AnonPages: 146521272 kB +Mapped: 41384 kB +Shmem: 105864 kB +Slab: 522648 kB +SReclaimable: 233508 kB +SUnreclaim: 289140 kB +KernelStack: 85024 kB +PageTables: 368760 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 79150268 kB +Committed_AS: 272491684 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 0 kB +VmallocChunk: 0 kB +HardwareCorrupted: 0 kB +AnonHugePages: 78061568 kB +ShmemHugePages: 0 kB +ShmemPmdMapped: 0 kB +CmaTotal: 0 kB +CmaFree: 0 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +DirectMap4k: 124388 kB +DirectMap2M: 5105664 kB +DirectMap1G: 147849216 kB +` + + meminfoFile := procd + "/meminfo" + err := ioutil.WriteFile(meminfoFile, []byte(meminfoContents), 0444) + if err != nil { + t.Fatal(err) + } + + mem := sigar.Mem{} + if assert.NoError(t, mem.Get()) { + assert.Equal(t, uint64(148535680*1024), mem.Total) + assert.Equal(t, uint64(417356*1024), mem.Free) + assert.Equal(t, uint64(0), mem.ActualFree) + } + + swap := sigar.Swap{} + if assert.NoError(t, swap.Get()) { + assert.Equal(t, uint64(4882428*1024), swap.Total) + assert.Equal(t, uint64(0), swap.Free) + } + +} + func TestFDUsage(t *testing.T) { setUp(t) defer tearDown(t)