Repository: prometheus/procfs
Pull request #203 (2019-08-18 10:27:57)

Add support for /proc/net/softnet_stat
softnet_stat contains important information for network performance
tuning. See these resources for details:
- https://blog.packagecloud.io/eng/2016/06/22/monitoring-tuning-linux-networking-stack-receiving-data/#monitoring-network-data-processing
- https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf
- https://github.com/leandromoreira/linux-network-performance-parameters#interrupt-coalescing-soft-irq-and-ingress-qdisc


Patch:
diff --git a/fixtures.ttar b/fixtures.ttar
index 8c4ad64..8591c27 100644
--- a/fixtures.ttar
+++ b/fixtures.ttar
@@ -1535,6 +1535,11 @@ proc4 2 2 10853
 proc4ops 72 0 0 0 1098 2 0 0 0 0 8179 5896 0 0 0 0 5900 0 0 2 0 2 0 9609 0 2 150 1272 0 0 0 1236 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/net/softnet_stat
+Lines: 1
+00015c73 00020e76 F0000769 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/net/unix
 Lines: 6
 Num       RefCount Protocol Flags    Type St Inode Path
diff --git a/net_softnet.go b/net_softnet.go
new file mode 100644
index 0000000..6fcad20
--- /dev/null
+++ b/net_softnet.go
@@ -0,0 +1,91 @@
+// 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 (
+	"fmt"
+	"io/ioutil"
+	"strconv"
+	"strings"
+)
+
+// For the proc file format details,
+// see https://elixir.bootlin.com/linux/v4.17/source/net/core/net-procfs.c#L162
+// and https://elixir.bootlin.com/linux/v4.17/source/include/linux/netdevice.h#L2810.
+
+// SoftnetEntry contains a single row of data from /proc/net/softnet_stat
+type SoftnetEntry struct {
+	// Number of processed packets
+	Processed uint
+	// Number of dropped packets
+	Dropped uint
+	// Number of times processing packets ran out of quota
+	TimeSqueezed uint
+}
+
+// GatherSoftnetStats reads /proc/net/softnet_stat, parse the relevant columns,
+// and then return a slice of SoftnetEntry's.
+func (fs FS) GatherSoftnetStats() ([]SoftnetEntry, error) {
+	data, err := ioutil.ReadFile(fs.proc.Path("net/softnet_stat"))
+	if err != nil {
+		return nil, fmt.Errorf("error reading softnet %s: %s", fs.proc.Path("net/softnet_stat"), err)
+	}
+
+	return parseSoftnetEntries(data)
+}
+
+func parseSoftnetEntries(data []byte) ([]SoftnetEntry, error) {
+	lines := strings.Split(string(data), "\n")
+	entries := make([]SoftnetEntry, 0)
+	var err error
+	const (
+		expectedColumns = 11
+	)
+	for _, line := range lines {
+		columns := strings.Fields(line)
+		width := len(columns)
+		if width == 0 {
+			continue
+		}
+		if width != expectedColumns {
+			return []SoftnetEntry{}, fmt.Errorf("%d columns were detected, but %d were expected", width, expectedColumns)
+		}
+		var entry SoftnetEntry
+		if entry, err = parseSoftnetEntry(columns); err != nil {
+			return []SoftnetEntry{}, err
+		}
+		entries = append(entries, entry)
+	}
+
+	return entries, nil
+}
+
+func parseSoftnetEntry(columns []string) (SoftnetEntry, error) {
+	var err error
+	var processed, dropped, timeSqueezed uint64
+	if processed, err = strconv.ParseUint(columns[0], 16, 32); err != nil {
+		return SoftnetEntry{}, fmt.Errorf("Unable to parse column 0: %s", err)
+	}
+	if dropped, err = strconv.ParseUint(columns[1], 16, 32); err != nil {
+		return SoftnetEntry{}, fmt.Errorf("Unable to parse column 1: %s", err)
+	}
+	if timeSqueezed, err = strconv.ParseUint(columns[2], 16, 32); err != nil {
+		return SoftnetEntry{}, fmt.Errorf("Unable to parse column 2: %s", err)
+	}
+	return SoftnetEntry{
+		Processed:    uint(processed),
+		Dropped:      uint(dropped),
+		TimeSqueezed: uint(timeSqueezed),
+	}, nil
+}


Test patch:
diff --git a/net_softnet_test.go b/net_softnet_test.go
new file mode 100644
index 0000000..a2658a3
--- /dev/null
+++ b/net_softnet_test.go
@@ -0,0 +1,42 @@
+// 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 (
+	"testing"
+)
+
+func TestSoftnet(t *testing.T) {
+	fs, err := NewFS(procTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	entries, err := fs.GatherSoftnetStats()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if want, got := uint(0x00015c73), entries[0].Processed; want != got {
+		t.Errorf("want %08x, got %08x", want, got)
+	}
+
+	if want, got := uint(0x00020e76), entries[0].Dropped; want != got {
+		t.Errorf("want %08x, got %08x", want, got)
+	}
+
+	if want, got := uint(0xF0000769), entries[0].TimeSqueezed; want != got {
+		t.Errorf("want %08x, got %08x", want, got)
+	}
+}