piframe-go/vendor/github.com/mackerelio/go-osstat/memory/memory_darwin.go

127 lines
3.3 KiB
Go

// +build darwin
package memory
import (
"bufio"
"context"
"fmt"
"io"
"os/exec"
"strconv"
"strings"
"time"
"unsafe"
"golang.org/x/sys/unix"
)
// Get memory statistics
func Get() (*Stats, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Reference: man 1 vm_stat
cmd := exec.CommandContext(ctx, "vm_stat")
out, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
if err := cmd.Start(); err != nil {
return nil, err
}
memory, err := collectMemoryStats(out)
if err != nil {
go cmd.Wait()
return nil, err
}
if err := cmd.Wait(); err != nil {
return nil, err
}
// Reference: sys/sysctl.h, man 3 sysctl, sysctl vm.swapusage
ret, err := unix.SysctlRaw("vm.swapusage")
if err != nil {
return nil, fmt.Errorf("failed in sysctl vm.swapusage: %s", err)
}
swap, err := collectSwapStats(ret)
if err != nil {
return nil, err
}
memory.SwapTotal = swap.Total
memory.SwapUsed = swap.Used
memory.SwapFree = swap.Avail
return memory, nil
}
// Stats represents memory statistics for darwin
type Stats struct {
Total, Used, Cached, Free, Active, Inactive, SwapTotal, SwapUsed, SwapFree uint64
}
// References:
// - https://support.apple.com/en-us/HT201464#memory
// - https://developer.apple.com/library/content/documentation/Performance/Conceptual/ManagingMemoryStats/Articles/AboutMemoryStats.html
// - https://opensource.apple.com/source/system_cmds/system_cmds-790/vm_stat.tproj/
func collectMemoryStats(out io.Reader) (*Stats, error) {
scanner := bufio.NewScanner(out)
if !scanner.Scan() {
return nil, fmt.Errorf("failed to scan output of vm_stat")
}
line := scanner.Text()
if !strings.HasPrefix(line, "Mach Virtual Memory Statistics:") {
return nil, fmt.Errorf("unexpected output of vm_stat: %s", line)
}
var memory Stats
var speculative, wired, purgeable, fileBacked, compressed uint64
memStats := map[string]*uint64{
"Pages free": &memory.Free,
"Pages active": &memory.Active,
"Pages inactive": &memory.Inactive,
"Pages speculative": &speculative,
"Pages wired down": &wired,
"Pages purgeable": &purgeable,
"File-backed pages": &fileBacked,
"Pages occupied by compressor": &compressed,
}
for scanner.Scan() {
line := scanner.Text()
i := strings.IndexRune(line, ':')
if i < 0 {
continue
}
if ptr := memStats[line[:i]]; ptr != nil {
val := strings.TrimRight(strings.TrimSpace(line[i+1:]), ".")
if v, err := strconv.ParseUint(val, 10, 64); err == nil {
*ptr = v * 4096
}
}
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("scan error for vm_stat: %s", err)
}
memory.Cached = purgeable + fileBacked
memory.Used = wired + compressed + memory.Active + memory.Inactive + speculative - memory.Cached
memory.Total = memory.Used + memory.Cached + memory.Free
return &memory, nil
}
// xsw_usage in sys/sysctl.h
type swapUsage struct {
Total uint64
Avail uint64
Used uint64
Pagesize int32
Encrypted bool
}
func collectSwapStats(out []byte) (*swapUsage, error) {
if len(out) != 32 {
return nil, fmt.Errorf("unexpected output of sysctl vm.swapusage: %v (len: %d)", out, len(out))
}
return (*swapUsage)(unsafe.Pointer(&out[0])), nil
}