diff --git a/mem/mem_darwin.go b/mem/mem_darwin.go index ba4dbd826..33147bba7 100644 --- a/mem/mem_darwin.go +++ b/mem/mem_darwin.go @@ -3,113 +3,12 @@ package mem import ( - "os/exec" "strconv" "strings" "github.com/shirou/gopsutil/internal/common" ) -func getPageSize() (uint64, error) { - out, err := exec.Command("pagesize").Output() - if err != nil { - return 0, err - } - o := strings.TrimSpace(string(out)) - p, err := strconv.ParseUint(o, 10, 64) - if err != nil { - return 0, err - } - return p, nil -} - -// Runs vm_stat and returns Free and inactive pages -func getVmStat(pagesize uint64, vms *VirtualMemoryStat) error { - out, err := exec.Command("vm_stat").Output() - if err != nil { - return err - } - return parseVmStat(string(out), pagesize, vms) -} - -func parseVmStat(out string, pagesize uint64, vms *VirtualMemoryStat) error { - var err error - - lines := strings.Split(out, "\n") - for _, line := range lines { - fields := strings.Split(line, ":") - if len(fields) < 2 { - continue - } - key := strings.TrimSpace(fields[0]) - value := strings.Trim(fields[1], " .") - switch key { - case "Pages free": - free, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Free = free * pagesize - case "Pages inactive": - inactive, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Cached += inactive * pagesize - vms.Inactive = inactive * pagesize - case "Pages active": - active, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Active = active * pagesize - case "Pages wired down": - wired, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Wired = wired * pagesize - case "Pages purgeable": - purgeable, e := strconv.ParseUint(value, 10, 64) - if e != nil { - err = e - } - vms.Cached += purgeable * pagesize - } - } - return err -} - -// VirtualMemory returns VirtualmemoryStat. -func VirtualMemory() (*VirtualMemoryStat, error) { - ret := &VirtualMemoryStat{} - - p, err := getPageSize() - if err != nil { - return nil, err - } - t, err := common.DoSysctrl("hw.memsize") - if err != nil { - return nil, err - } - total, err := strconv.ParseUint(t[0], 10, 64) - if err != nil { - return nil, err - } - err = getVmStat(p, ret) - if err != nil { - return nil, err - } - - ret.Available = ret.Free + ret.Cached - ret.Total = total - - ret.Used = ret.Total - ret.Free - ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 - - return ret, nil -} - // SwapMemory returns swapinfo. func SwapMemory() (*SwapMemoryStat, error) { var ret *SwapMemoryStat diff --git a/mem/mem_darwin_cgo.go b/mem/mem_darwin_cgo.go new file mode 100644 index 000000000..1d0852818 --- /dev/null +++ b/mem/mem_darwin_cgo.go @@ -0,0 +1,52 @@ +// +build darwin +// +build cgo + +package mem + +/* +#include +*/ +import "C" + +import ( + "fmt" + "syscall" + "unsafe" +) + +// VirtualMemory returns VirtualmemoryStat. +func VirtualMemory() (*VirtualMemoryStat, error) { + count := C.mach_msg_type_number_t(C.HOST_VM_INFO_COUNT) + var vmstat C.vm_statistics_data_t + + status := C.host_statistics(C.host_t(C.mach_host_self()), + C.HOST_VM_INFO, + C.host_info_t(unsafe.Pointer(&vmstat)), + &count) + + if status != C.KERN_SUCCESS { + return nil, fmt.Errorf("host_statistics error=%d", status) + } + + totalCount := vmstat.wire_count + + vmstat.active_count + + vmstat.inactive_count + + vmstat.free_count + + availableCount := vmstat.inactive_count + vmstat.free_count + usedPercent := 100 * float64(totalCount-availableCount) / float64(totalCount) + + usedCount := totalCount - vmstat.free_count + + pageSize := uint64(syscall.Getpagesize()) + return &VirtualMemoryStat{ + Total: pageSize * uint64(totalCount), + Available: pageSize * uint64(availableCount), + Used: pageSize * uint64(usedCount), + UsedPercent: usedPercent, + Free: pageSize * uint64(vmstat.free_count), + Active: pageSize * uint64(vmstat.active_count), + Inactive: pageSize * uint64(vmstat.inactive_count), + Wired: pageSize * uint64(vmstat.wire_count), + }, nil +} diff --git a/mem/mem_darwin_nocgo.go b/mem/mem_darwin_nocgo.go new file mode 100644 index 000000000..d1bd4f657 --- /dev/null +++ b/mem/mem_darwin_nocgo.go @@ -0,0 +1,90 @@ +// +build darwin +// +build !cgo + +package mem + +import ( + "os/exec" + "strconv" + "strings" + "syscall" + + "github.com/shirou/gopsutil/internal/common" +) + +// Runs vm_stat and returns Free and inactive pages +func getVMStat(vms *VirtualMemoryStat) error { + out, err := exec.Command("vm_stat").Output() + if err != nil { + return err + } + return parseVMStat(string(out), vms) +} + +func parseVMStat(out string, vms *VirtualMemoryStat) error { + var err error + + lines := strings.Split(out, "\n") + pagesize := uint64(syscall.Getpagesize()) + for _, line := range lines { + fields := strings.Split(line, ":") + if len(fields) < 2 { + continue + } + key := strings.TrimSpace(fields[0]) + value := strings.Trim(fields[1], " .") + switch key { + case "Pages free": + free, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Free = free * pagesize + case "Pages inactive": + inactive, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Inactive = inactive * pagesize + case "Pages active": + active, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Active = active * pagesize + case "Pages wired down": + wired, e := strconv.ParseUint(value, 10, 64) + if e != nil { + err = e + } + vms.Wired = wired * pagesize + } + } + return err +} + +// VirtualMemory returns VirtualmemoryStat. +func VirtualMemory() (*VirtualMemoryStat, error) { + ret := &VirtualMemoryStat{} + + t, err := common.DoSysctrl("hw.memsize") + if err != nil { + return nil, err + } + total, err := strconv.ParseUint(t[0], 10, 64) + if err != nil { + return nil, err + } + err = getVMStat(ret) + if err != nil { + return nil, err + } + + ret.Available = ret.Free + ret.Inactive + ret.Total = total + + ret.Used = ret.Total - ret.Free + ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 + + return ret, nil +} diff --git a/mem/mem_darwin_test.go b/mem/mem_darwin_test.go index 730dc52f3..0f2ea3a93 100644 --- a/mem/mem_darwin_test.go +++ b/mem/mem_darwin_test.go @@ -4,64 +4,38 @@ package mem import ( "testing" + + "github.com/stretchr/testify/assert" ) -var vm_stat_out = ` -Mach Virtual Memory Statistics: (page size of 4096 bytes) -Pages free: 105885. -Pages active: 725641. -Pages inactive: 449242. -Pages speculative: 6155. -Pages throttled: 0. -Pages wired down: 560835. -Pages purgeable: 128967. -"Translation faults": 622528839. -Pages copy-on-write: 17697839. -Pages zero filled: 311034413. -Pages reactivated: 4705104. -Pages purged: 5605610. -File-backed pages: 349192. -Anonymous pages: 831846. -Pages stored in compressor: 876507. -Pages occupied by compressor: 249167. -Decompressions: 4555025. -Compressions: 7524729. -Pageins: 40532443. -Pageouts: 126496. -Swapins: 2988073. -Swapouts: 3283599. -` +func TestVirtualMemoryDarwin(t *testing.T) { + v, err := VirtualMemory() + assert.Nil(t, err) + + assert.True(t, v.Total > 0) + + assert.Equal(t, v.Total, v.Active+v.Inactive+v.Free+v.Wired) -func TestParseVmStat(t *testing.T) { - ret := &VirtualMemoryStat{} - err := parseVmStat(vm_stat_out, 4096, ret) + assert.True(t, v.Available > 0) + assert.True(t, v.Available < v.Total) + assert.Equal(t, v.Available, v.Total-v.Wired-v.Active, "%v", v) + assert.Equal(t, v.Available, v.Free+v.Inactive, "%v", v) - if err != nil { - t.Errorf("Expected no error, got %s\n", err.Error()) - } + assert.True(t, v.Used > 0) + assert.True(t, v.Used < v.Total) - if ret.Free != uint64(105885*4096) { - t.Errorf("Free pages, actual: %d, expected: %d", ret.Free, - 105885*4096) - } + assert.True(t, v.UsedPercent > 0) + assert.True(t, v.UsedPercent < 100) - if ret.Inactive != uint64(449242*4096) { - t.Errorf("Inactive pages, actual: %d, expected: %d", ret.Inactive, - 449242*4096) - } + assert.True(t, v.Free > 0) + assert.True(t, v.Free < v.Available) - if ret.Active != uint64(725641*4096) { - t.Errorf("Active pages, actual: %d, expected: %d", ret.Active, - 725641*4096) - } + assert.True(t, v.Active > 0) + assert.True(t, v.Active < v.Total) - if ret.Wired != uint64(560835*4096) { - t.Errorf("Wired pages, actual: %d, expected: %d", ret.Wired, - 560835*4096) - } + assert.True(t, v.Inactive > 0) + assert.True(t, v.Inactive < v.Total) - if ret.Cached != uint64(128967*4096+449242.*4096) { - t.Errorf("Cached pages, actual: %d, expected: %f", ret.Cached, - 128967*4096+449242.*4096) - } + assert.True(t, v.Wired > 0) + assert.True(t, v.Wired < v.Total) }