Skip to content

Commit

Permalink
Eliminate call to swapinfo on FreeBSD
Browse files Browse the repository at this point in the history
Improve performance by eliminating the fork out to swapinfo on FreeBSD which also helps prevent crashes / hangs due to the outstanding fork crash bug:
golang/go#15658

This also fixes the value reported by SwapMemory and SwapMemoryWithContext on FreeBSD which previously only included the first swap device and also reported the values in terms of 1K blocks instead of bytes.
  • Loading branch information
stevenh committed Mar 11, 2018
1 parent 5776ff9 commit d968f63
Showing 1 changed file with 43 additions and 32 deletions.
75 changes: 43 additions & 32 deletions mem/mem_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ package mem
import (
"context"
"errors"
"os/exec"
"strconv"
"strings"
"unsafe"

"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -69,53 +67,66 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
}

// Return swapinfo
// FreeBSD can have multiple swap devices. but use only first device
func SwapMemory() (*SwapMemoryStat, error) {
return SwapMemoryWithContext(context.Background())
}

// Constants from vm/vm_param.h
// nolint: golint
const (
XSWDEV_VERSION = 1
)

// Types from vm/vm_param.h
type xswdev struct {
Version uint32 // Version is the version
Dev uint32 // Dev is the device identifier
Flags int32 // Flags is the swap flags applied to the device
NBlks int32 // NBlks is the total number of blocks
Used int32 // Used is the number of blocks used
}

func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
swapinfo, err := exec.LookPath("swapinfo")
// FreeBSD can have multiple swap devices so we total them up
i, err := unix.SysctlUint32("vm.nswapdev")
if err != nil {
return nil, err
}

out, err := invoke.Command(swapinfo)
if i == 0 {
return nil, errors.New("no swap devices found")
}

c := int(i)

i, err = unix.SysctlUint32("vm.stats.vm.v_page_size")
if err != nil {
return nil, err
}
for _, line := range strings.Split(string(out), "\n") {
values := strings.Fields(line)
// skip title line
if len(values) == 0 || values[0] == "Device" {
continue
}
pageSize := uint64(i)

u := strings.Replace(values[4], "%", "", 1)
total_v, err := strconv.ParseUint(values[1], 10, 64)
if err != nil {
return nil, err
}
used_v, err := strconv.ParseUint(values[2], 10, 64)
if err != nil {
return nil, err
}
free_v, err := strconv.ParseUint(values[3], 10, 64)
var buf []byte
s := &SwapMemoryStat{}
for n := 0; n < c; n++ {
buf, err = unix.SysctlRaw("vm.swap_info", n)
if err != nil {
return nil, err
}
up_v, err := strconv.ParseFloat(u, 64)
if err != nil {
return nil, err

xsw := (*xswdev)(unsafe.Pointer(&buf[0]))
if xsw.Version != XSWDEV_VERSION {
return nil, errors.New("xswdev version mismatch")
}
s.Total += uint64(xsw.NBlks)
s.Used += uint64(xsw.Used)
}

return &SwapMemoryStat{
Total: total_v,
Used: used_v,
Free: free_v,
UsedPercent: up_v,
}, nil
if s.Total != 0 {
s.UsedPercent = float64(s.Used) / float64(s.Total) * 100
}
s.Total *= pageSize
s.Used *= pageSize
s.Free = s.Total - s.Used

return nil, errors.New("no swap devices found")
return s, nil
}

0 comments on commit d968f63

Please sign in to comment.