Skip to content

Commit

Permalink
Merge pull request shirou#1117 from tklauser/update-go-sysconf
Browse files Browse the repository at this point in the history
Update github.com/tklauser/go-sysconf to v0.3.8
  • Loading branch information
shirou authored and tbarker25 committed Aug 19, 2021
2 parents ff3e668 + cb29e40 commit 7ad5a5f
Show file tree
Hide file tree
Showing 19 changed files with 540 additions and 18 deletions.
6 changes: 3 additions & 3 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Gopkg.toml
Expand Up @@ -35,7 +35,7 @@

[[constraint]]
name = "github.com/tklauser/go-sysconf"
version = "0.3.6"
version = "0.3.8"

[[constraint]]
branch = "master"
Expand Down
4 changes: 2 additions & 2 deletions v3/go.mod
Expand Up @@ -5,6 +5,6 @@ go 1.15
require (
github.com/StackExchange/wmi v1.2.1
github.com/stretchr/testify v1.7.0
github.com/tklauser/go-sysconf v0.3.7
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
github.com/tklauser/go-sysconf v0.3.8
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71
)
7 changes: 4 additions & 3 deletions v3/go.sum
Expand Up @@ -9,13 +9,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tklauser/go-sysconf v0.3.7 h1:HT7h4+536gjqeq1ZIJPgOl1rg1XFatQGVZWp7Py53eg=
github.com/tklauser/go-sysconf v0.3.7/go.mod h1:JZIdXh4RmBvZDBZ41ld2bGxRV3n4daiiqA3skYhAoQ4=
github.com/tklauser/go-sysconf v0.3.8 h1:41Nq9J+pxKud4IQ830J5LlS5nl67dVQC7AuisUooaOU=
github.com/tklauser/go-sysconf v0.3.8/go.mod h1:z4zYWRS+X53WUKtBcmDg1comV3fPhdQnzasnIHUoLDU=
github.com/tklauser/numcpus v0.2.3 h1:nQ0QYpiritP6ViFhrKYsiv6VVxOpum2Gks5GhnJbS/8=
github.com/tklauser/numcpus v0.2.3/go.mod h1:vpEPS/JC+oZGGQ/My/vJnNsvMDQL6PwOqt8dsCw5j+E=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 h1:ikCpsnYR+Ew0vu99XlDp55lGgDJdIMx3f4a18jfse/s=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
Expand Down
11 changes: 11 additions & 0 deletions v3/mem/mem.go
Expand Up @@ -103,3 +103,14 @@ func (m SwapMemoryStat) String() string {
s, _ := json.Marshal(m)
return string(s)
}

type SwapDevice struct {
Name string `json:"name"`
UsedBytes uint64 `json:"usedBytes"`
FreeBytes uint64 `json:"freeBytes"`
}

func (m SwapDevice) String() string {
s, _ := json.Marshal(m)
return string(s)
}
87 changes: 87 additions & 0 deletions v3/mem/mem_bsd.go
@@ -0,0 +1,87 @@
// +build freebsd openbsd

package mem

import (
"context"
"fmt"
"os/exec"
"strconv"
"strings"
)

const swapCommand = "/sbin/swapctl"

// swapctl column indexes
const (
nameCol = 0
totalKiBCol = 1
usedKiBCol = 2
)

func SwapDevices() ([]*SwapDevice, error) {
return SwapDevicesWithContext(context.Background())
}

func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
output, err := exec.Command(swapCommand, "-lk").Output()
if err != nil {
return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err)
}

return parseSwapctlOutput(string(output))
}

func parseSwapctlOutput(output string) ([]*SwapDevice, error) {
lines := strings.Split(output, "\n")
if len(lines) == 0 {
return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output)
}

// Check header headerFields are as expected.
header := lines[0]
header = strings.ToLower(header)
header = strings.ReplaceAll(header, ":", "")
headerFields := strings.Fields(header)
if len(headerFields) < usedKiBCol {
return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, header)
}
if headerFields[nameCol] != "device" {
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "device")
}
if headerFields[totalKiBCol] != "1kb-blocks" && headerFields[totalKiBCol] != "1k-blocks" {
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalKiBCol], "1kb-blocks")
}
if headerFields[usedKiBCol] != "used" {
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[usedKiBCol], "used")
}

var swapDevices []*SwapDevice
for _, line := range lines[1:] {
if line == "" {
continue // the terminal line is typically empty
}
fields := strings.Fields(line)
if len(fields) < usedKiBCol {
return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand)
}

totalKiB, err := strconv.ParseUint(fields[totalKiBCol], 10, 64)
if err != nil {
return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err)
}

usedKiB, err := strconv.ParseUint(fields[usedKiBCol], 10, 64)
if err != nil {
return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err)
}

swapDevices = append(swapDevices, &SwapDevice{
Name: fields[nameCol],
UsedBytes: usedKiB * 1024,
FreeBytes: (totalKiB - usedKiB) * 1024,
})
}

return swapDevices, nil
}
63 changes: 63 additions & 0 deletions v3/mem/mem_bsd_test.go
@@ -0,0 +1,63 @@
// +build freebsd openbsd

package mem

import (
"testing"

"github.com/stretchr/testify/assert"
)

const validFreeBSD = `Device: 1kB-blocks Used:
/dev/gpt/swapfs 1048576 1234
/dev/md0 1048576 666
`

const validOpenBSD = `Device 1K-blocks Used Avail Capacity Priority
/dev/wd0b 655025 1234 653791 1% 0
`

const invalid = `Device: 512-blocks Used:
/dev/gpt/swapfs 1048576 1234
/dev/md0 1048576 666
`

func TestParseSwapctlOutput_FreeBSD(t *testing.T) {
assert := assert.New(t)
stats, err := parseSwapctlOutput(validFreeBSD)
assert.NoError(err)

assert.Equal(*stats[0], SwapDevice{
Name: "/dev/gpt/swapfs",
UsedBytes: 1263616,
FreeBytes: 1072478208,
})

assert.Equal(*stats[1], SwapDevice{
Name: "/dev/md0",
UsedBytes: 681984,
FreeBytes: 1073059840,
})
}

func TestParseSwapctlOutput_OpenBSD(t *testing.T) {
assert := assert.New(t)
stats, err := parseSwapctlOutput(validOpenBSD)
assert.NoError(err)

assert.Equal(*stats[0], SwapDevice{
Name: "/dev/wd0b",
UsedBytes: 1234 * 1024,
FreeBytes: 653791 * 1024,
})
}

func TestParseSwapctlOutput_Invalid(t *testing.T) {
_, err := parseSwapctlOutput(invalid)
assert.Error(t, err)
}

func TestParseSwapctlOutput_Empty(t *testing.T) {
_, err := parseSwapctlOutput("")
assert.Error(t, err)
}
9 changes: 9 additions & 0 deletions v3/mem/mem_darwin.go
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"unsafe"

"github.com/shirou/gopsutil/v3/internal/common"
"golang.org/x/sys/unix"
)

Expand Down Expand Up @@ -67,3 +68,11 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {

return ret, nil
}

func SwapDevices() ([]*SwapDevice, error) {
return SwapDevicesWithContext(context.Background())
}

func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
return nil, common.ErrNotImplementedError
}
3 changes: 1 addition & 2 deletions v3/mem/mem_darwin_cgo.go
@@ -1,5 +1,4 @@
// +build darwin
// +build cgo
// +build darwin,cgo

package mem

Expand Down
3 changes: 1 addition & 2 deletions v3/mem/mem_darwin_nocgo.go
@@ -1,5 +1,4 @@
// +build darwin
// +build !cgo
// +build darwin,!cgo

package mem

Expand Down
8 changes: 8 additions & 0 deletions v3/mem/mem_fallback.go
Expand Up @@ -23,3 +23,11 @@ func SwapMemory() (*SwapMemoryStat, error) {
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
return nil, common.ErrNotImplementedError
}

func SwapDevices() ([]*SwapDevice, error) {
return SwapDevicesWithContext(context.Background())
}

func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
return nil, common.ErrNotImplementedError
}
84 changes: 84 additions & 0 deletions v3/mem/mem_linux.go
Expand Up @@ -3,8 +3,11 @@
package mem

import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"math"
"os"
"strconv"
Expand Down Expand Up @@ -426,3 +429,84 @@ func calcuateAvailVmem(ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint6

return availMemory
}

const swapsFilePath = "/proc/swaps"

// swaps file column indexes
const (
nameCol = 0
// typeCol = 1
totalCol = 2
usedCol = 3
// priorityCol = 4
)

func SwapDevices() ([]*SwapDevice, error) {
return SwapDevicesWithContext(context.Background())
}

func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
f, err := os.Open(swapsFilePath)
if err != nil {
return nil, err
}
defer f.Close()

return parseSwapsFile(f)
}

func parseSwapsFile(r io.Reader) ([]*SwapDevice, error) {
scanner := bufio.NewScanner(r)
if !scanner.Scan() {
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("couldn't read file %q: %w", swapsFilePath, err)
}
return nil, fmt.Errorf("unexpected end-of-file in %q", swapsFilePath)

}

// Check header headerFields are as expected
headerFields := strings.Fields(scanner.Text())
if len(headerFields) < usedCol {
return nil, fmt.Errorf("couldn't parse %q: too few fields in header", swapsFilePath)
}
if headerFields[nameCol] != "Filename" {
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[nameCol], "Filename")
}
if headerFields[totalCol] != "Size" {
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[totalCol], "Size")
}
if headerFields[usedCol] != "Used" {
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapsFilePath, headerFields[usedCol], "Used")
}

var swapDevices []*SwapDevice
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) < usedCol {
return nil, fmt.Errorf("couldn't parse %q: too few fields", swapsFilePath)
}

totalKiB, err := strconv.ParseUint(fields[totalCol], 10, 64)
if err != nil {
return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapsFilePath, err)
}

usedKiB, err := strconv.ParseUint(fields[usedCol], 10, 64)
if err != nil {
return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapsFilePath, err)
}

swapDevices = append(swapDevices, &SwapDevice{
Name: fields[nameCol],
UsedBytes: usedKiB * 1024,
FreeBytes: (totalKiB - usedKiB) * 1024,
})
}

if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("couldn't read file %q: %w", swapsFilePath, err)
}

return swapDevices, nil
}

0 comments on commit 7ad5a5f

Please sign in to comment.