Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions fixtures.ttar
Original file line number Diff line number Diff line change
Expand Up @@ -1952,6 +1952,12 @@ procs_blocked 1
softirq 5057579 250191 1481983 1647 211099 186066 0 1783454 622196 12499 508444
Mode: 644
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/proc/swaps
Lines: 2
Filename Type Size Used Priority
/dev/dm-2 partition 131068 176 -2
Mode: 444
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Directory: fixtures/proc/symlinktargets
Mode: 755
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand Down
89 changes: 89 additions & 0 deletions swaps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2019 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package procfs

import (
"bufio"
"bytes"
"fmt"
"strconv"
"strings"

"github.com/prometheus/procfs/internal/util"
)

// Swap represents an entry in /proc/swaps.
type Swap struct {
Filename string
Type string
Size int
Used int
Priority int
}

// Swaps returns a slice of all configured swap devices on the system.
func (fs FS) Swaps() ([]*Swap, error) {
data, err := util.ReadFileNoStat(fs.proc.Path("swaps"))
if err != nil {
return nil, err
}
return parseSwaps(data)
}

func parseSwaps(info []byte) ([]*Swap, error) {
swaps := []*Swap{}
scanner := bufio.NewScanner(bytes.NewReader(info))
scanner.Scan() // ignore header line
for scanner.Scan() {
swapString := scanner.Text()
parsedSwap, err := parseSwapString(swapString)
if err != nil {
return nil, err
}
swaps = append(swaps, parsedSwap)
}

err := scanner.Err()
return swaps, err
}

func parseSwapString(swapString string) (*Swap, error) {
var err error

swapFields := strings.Fields(swapString)
swapLength := len(swapFields)
if swapLength < 5 {
return nil, fmt.Errorf("too few fields in swap string: %s", swapString)
}

swap := &Swap{
Filename: swapFields[0],
Type: swapFields[1],
}

swap.Size, err = strconv.Atoi(swapFields[2])
if err != nil {
return nil, fmt.Errorf("invalid swap size: %s", swapFields[2])
}
swap.Used, err = strconv.Atoi(swapFields[3])
if err != nil {
return nil, fmt.Errorf("invalid swap used: %s", swapFields[3])
}
swap.Priority, err = strconv.Atoi(swapFields[4])
if err != nil {
return nil, fmt.Errorf("invalid swap priority: %s", swapFields[4])
}

return swap, nil
}
113 changes: 113 additions & 0 deletions swaps_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2019 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package procfs

import (
"reflect"
"testing"
)

func TestSwaps(t *testing.T) {
fs, err := NewFS(procTestFixtures)
if err != nil {
t.Fatalf("failed to open procfs: %v", err)
}

swaps, err := fs.Swaps()
if err != nil {
t.Fatalf("failed to get swaps: %v", err)
}

if len(swaps) != 1 {
t.Fatalf("expected 1 swap entry, got %d", len(swaps))
}
swap := swaps[0]

if swap.Filename != "/dev/dm-2" {
t.Errorf("expected swap.Filename /dev/dm-2, got %s", swap.Filename)
}
if swap.Type != "partition" {
t.Errorf("expected swap.Type partition, got %s", swap.Type)
}
if swap.Size != 131068 {
t.Errorf("expected swap.Size 131068, got %d", swap.Size)
}
if swap.Used != 176 {
t.Errorf("expected swap.Used 176, got %d", swap.Used)
}
if swap.Priority != -2 {
t.Errorf("expected swap.Priority -2, got %d", swap.Priority)
}
}

func TestParseSwapString(t *testing.T) {
tests := []struct {
name string
s string
swap *Swap
invalid bool
}{
{
name: "device-mapper volume",
s: "/dev/dm-2 partition 131068 1024 -2",
invalid: false,
swap: &Swap{
Filename: "/dev/dm-2",
Type: "partition",
Size: 131068,
Used: 1024,
Priority: -2,
},
},
{
name: "Swap file",
s: "/foo file 1048572 0 -3",
invalid: false,
swap: &Swap{
Filename: "/foo",
Type: "file",
Size: 1048572,
Used: 0,
Priority: -3,
},
},
{
name: "Invalid number",
s: "/dev/sda2 partition hello world -2",
invalid: true,
},
{
name: "Not enough fields",
s: "/dev/dm-2 partition 131068 1024",
invalid: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
swap, err := parseSwapString(tt.s)

if tt.invalid && err == nil {
t.Error("unexpected success")
}
if !tt.invalid && err != nil {
t.Errorf("unexpected error: %v", err)
}

if !reflect.DeepEqual(tt.swap, swap) {
t.Errorf("swap:\nwant:\n%+v\nhave:\n%+v", tt.swap, swap)
}
})
}
}