Repository: prometheus/procfs
Pull request #588 (2023-11-16 08:54:48)

meminfo: add Percpu field
Signed-off-by: wangqing <wangqing@uniontech.com>

@discordianfish

@SuperQ 


Patch:
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index babd8a0..ffa6b30 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -22,7 +22,7 @@ jobs:
       - name: install Go
         uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
         with:
-          go-version: 1.20.x
+          go-version: 1.21.x
       - name: Install snmp_exporter/generator dependencies
         run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
         if: github.repository == 'prometheus/snmp_exporter'
diff --git a/Makefile.common b/Makefile.common
index 062a281..bc2a07d 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -61,7 +61,7 @@ PROMU_URL     := https://github.com/prometheus/promu/releases/download/v$(PROMU_
 SKIP_GOLANGCI_LINT :=
 GOLANGCI_LINT :=
 GOLANGCI_LINT_OPTS ?=
-GOLANGCI_LINT_VERSION ?= v1.54.2
+GOLANGCI_LINT_VERSION ?= v1.55.2
 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
 # windows isn't included here because of the path separator being different.
 ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
diff --git a/crypto.go b/crypto.go
index 9a73e26..13ee66f 100644
--- a/crypto.go
+++ b/crypto.go
@@ -84,7 +84,7 @@ func parseCrypto(r io.Reader) ([]Crypto, error) {
 
 		kv := strings.Split(text, ":")
 		if len(kv) != 2 {
-			return nil, fmt.Errorf("%w: Cannot parae line: %q", ErrFileParse, text)
+			return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, text)
 		}
 
 		k := strings.TrimSpace(kv[0])
diff --git a/meminfo.go b/meminfo.go
index db1daea..73a03e8 100644
--- a/meminfo.go
+++ b/meminfo.go
@@ -126,6 +126,7 @@ type Meminfo struct {
 	VmallocUsed *uint64
 	// largest contiguous block of vmalloc area which is free
 	VmallocChunk      *uint64
+	Percpu            *uint64
 	HardwareCorrupted *uint64
 	AnonHugePages     *uint64
 	ShmemHugePages    *uint64
@@ -178,6 +179,7 @@ type Meminfo struct {
 	VmallocTotalBytes      *uint64
 	VmallocUsedBytes       *uint64
 	VmallocChunkBytes      *uint64
+	PercpuBytes            *uint64
 	HardwareCorruptedBytes *uint64
 	AnonHugePagesBytes     *uint64
 	ShmemHugePagesBytes    *uint64
@@ -339,6 +341,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
 		case "VmallocChunk:":
 			m.VmallocChunk = &val
 			m.VmallocChunkBytes = &valBytes
+		case "Percpu:":
+			m.Percpu = &val
+			m.PercpuBytes = &valBytes
 		case "HardwareCorrupted:":
 			m.HardwareCorrupted = &val
 			m.HardwareCorruptedBytes = &valBytes
diff --git a/net_ip_socket.go b/net_ip_socket.go
index 4da81ea..ba7d9ca 100644
--- a/net_ip_socket.go
+++ b/net_ip_socket.go
@@ -50,10 +50,13 @@ type (
 		// UsedSockets shows the total number of parsed lines representing the
 		// number of used sockets.
 		UsedSockets uint64
+		// Drops shows the total number of dropped packets of all UPD sockets.
+		Drops *uint64
 	}
 
 	// netIPSocketLine represents the fields parsed from a single line
 	// in /proc/net/{t,u}dp{,6}. Fields which are not used by IPSocket are skipped.
+	// Drops is non-nil for udp{,6}, but nil for tcp{,6}.
 	// For the proc file format details, see https://linux.die.net/man/5/proc.
 	netIPSocketLine struct {
 		Sl        uint64
@@ -66,6 +69,7 @@ type (
 		RxQueue   uint64
 		UID       uint64
 		Inode     uint64
+		Drops     *uint64
 	}
 )
 
@@ -77,13 +81,14 @@ func newNetIPSocket(file string) (NetIPSocket, error) {
 	defer f.Close()
 
 	var netIPSocket NetIPSocket
+	isUDP := strings.Contains(file, "udp")
 
 	lr := io.LimitReader(f, readLimit)
 	s := bufio.NewScanner(lr)
 	s.Scan() // skip first line with headers
 	for s.Scan() {
 		fields := strings.Fields(s.Text())
-		line, err := parseNetIPSocketLine(fields)
+		line, err := parseNetIPSocketLine(fields, isUDP)
 		if err != nil {
 			return nil, err
 		}
@@ -104,19 +109,25 @@ func newNetIPSocketSummary(file string) (*NetIPSocketSummary, error) {
 	defer f.Close()
 
 	var netIPSocketSummary NetIPSocketSummary
+	var udpPacketDrops uint64
+	isUDP := strings.Contains(file, "udp")
 
 	lr := io.LimitReader(f, readLimit)
 	s := bufio.NewScanner(lr)
 	s.Scan() // skip first line with headers
 	for s.Scan() {
 		fields := strings.Fields(s.Text())
-		line, err := parseNetIPSocketLine(fields)
+		line, err := parseNetIPSocketLine(fields, isUDP)
 		if err != nil {
 			return nil, err
 		}
 		netIPSocketSummary.TxQueueLength += line.TxQueue
 		netIPSocketSummary.RxQueueLength += line.RxQueue
 		netIPSocketSummary.UsedSockets++
+		if isUDP {
+			udpPacketDrops += *line.Drops
+			netIPSocketSummary.Drops = &udpPacketDrops
+		}
 	}
 	if err := s.Err(); err != nil {
 		return nil, err
@@ -149,7 +160,7 @@ func parseIP(hexIP string) (net.IP, error) {
 }
 
 // parseNetIPSocketLine parses a single line, represented by a list of fields.
-func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) {
+func parseNetIPSocketLine(fields []string, isUDP bool) (*netIPSocketLine, error) {
 	line := &netIPSocketLine{}
 	if len(fields) < 10 {
 		return nil, fmt.Errorf(
@@ -224,5 +235,14 @@ func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) {
 		return nil, fmt.Errorf("%s: Cannot parse inode value in %q: %w", ErrFileParse, line.Inode, err)
 	}
 
+	// drops
+	if isUDP {
+		drops, err := strconv.ParseUint(fields[12], 0, 64)
+		if err != nil {
+			return nil, fmt.Errorf("%s: Cannot parse drops value in %q: %w", ErrFileParse, drops, err)
+		}
+		line.Drops = &drops
+	}
+
 	return line, nil
 }


Test patch:
diff --git a/meminfo_test.go b/meminfo_test.go
index 2d2b238..3d3b409 100644
--- a/meminfo_test.go
+++ b/meminfo_test.go
@@ -53,6 +53,7 @@ func TestMeminfo(t *testing.T) {
 		VmallocTotal:      newuint64(34359738367),
 		VmallocUsed:       newuint64(36596),
 		VmallocChunk:      newuint64(34359637840),
+		Percpu:            newuint64(26176),
 		HardwareCorrupted: newuint64(0),
 		AnonHugePages:     newuint64(12288),
 		HugePagesTotal:    newuint64(0),
@@ -96,6 +97,7 @@ func TestMeminfo(t *testing.T) {
 		VmallocTotalBytes:      newuint64(35184372087808),
 		VmallocUsedBytes:       newuint64(37474304),
 		VmallocChunkBytes:      newuint64(35184269148160),
+		PercpuBytes:            newuint64(26804224),
 		HardwareCorruptedBytes: newuint64(0),
 		AnonHugePagesBytes:     newuint64(12582912),
 		HugepagesizeBytes:      newuint64(2097152),
diff --git a/net_ip_socket_test.go b/net_ip_socket_test.go
index 9bbad87..3c057fb 100644
--- a/net_ip_socket_test.go
+++ b/net_ip_socket_test.go
@@ -25,6 +25,7 @@ func Test_parseNetIPSocketLine(t *testing.T) {
 		name    string
 		want    *netIPSocketLine
 		wantErr bool
+		isUDP   bool
 	}{
 		{
 			name:   "reading valid lines, no issue should happened",
@@ -96,10 +97,17 @@ func Test_parseNetIPSocketLine(t *testing.T) {
 			want:    nil,
 			wantErr: true,
 		},
+		{
+			name:    "error case - parse Drops - not a valid uint",
+			fields:  []string{"1:", "00000000:0000", "00000000:0000", "07", "00000000:00000001", "0:0", "0", "10", "0", "39309", "2", "000000009bd60d72", "-5"},
+			want:    nil,
+			wantErr: true,
+			isUDP:   true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := parseNetIPSocketLine(tt.fields)
+			got, err := parseNetIPSocketLine(tt.fields, tt.isUDP)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("parseNetIPSocketLine() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/net_udp_test.go b/net_udp_test.go
index 9abb64d..6576161 100644
--- a/net_udp_test.go
+++ b/net_udp_test.go
@@ -41,6 +41,7 @@ func Test_newNetUDP(t *testing.T) {
 					RxQueue:   1,
 					UID:       0,
 					Inode:     2740,
+					Drops:     intToU64(100),
 				},
 				&netIPSocketLine{
 					Sl:        1,
@@ -53,6 +54,7 @@ func Test_newNetUDP(t *testing.T) {
 					RxQueue:   0,
 					UID:       0,
 					Inode:     2740,
+					Drops:     intToU64(100),
 				},
 				&netIPSocketLine{
 					Sl:        2,
@@ -65,6 +67,7 @@ func Test_newNetUDP(t *testing.T) {
 					RxQueue:   1,
 					UID:       0,
 					Inode:     2740,
+					Drops:     intToU64(100),
 				},
 			},
 			wantErr: false,
@@ -84,6 +87,7 @@ func Test_newNetUDP(t *testing.T) {
 					RxQueue:   0,
 					UID:       981,
 					Inode:     21040,
+					Drops:     intToU64(0),
 				},
 				&netIPSocketLine{
 					Sl:        6073,
@@ -96,6 +100,7 @@ func Test_newNetUDP(t *testing.T) {
 					RxQueue:   0,
 					UID:       1000,
 					Inode:     11337031,
+					Drops:     intToU64(0),
 				},
 			},
 			wantErr: false,
@@ -137,13 +142,13 @@ func Test_newNetUDPSummary(t *testing.T) {
 		{
 			name:    "udp file found, no error should come up",
 			file:    "testdata/fixtures/proc/net/udp",
-			want:    &NetUDPSummary{TxQueueLength: 2, RxQueueLength: 2, UsedSockets: 3},
+			want:    &NetUDPSummary{TxQueueLength: 2, RxQueueLength: 2, UsedSockets: 3, Drops: intToU64(300)},
 			wantErr: false,
 		},
 		{
 			name:    "udp6 file found, no error should come up",
 			file:    "testdata/fixtures/proc/net/udp6",
-			want:    &NetUDPSummary{TxQueueLength: 0, RxQueueLength: 0, UsedSockets: 2},
+			want:    &NetUDPSummary{TxQueueLength: 0, RxQueueLength: 0, UsedSockets: 2, Drops: intToU64(0)},
 			wantErr: false,
 		},
 		{
@@ -172,3 +177,9 @@ func Test_newNetUDPSummary(t *testing.T) {
 		})
 	}
 }
+
+// intToU64 convert int to uint64 and return it pointer.
+func intToU64(i int) *uint64 {
+	cast := uint64(i)
+	return &cast
+}
diff --git a/testdata/fixtures.ttar b/testdata/fixtures.ttar
index 92f72fa..6706516 100644
--- a/testdata/fixtures.ttar
+++ b/testdata/fixtures.ttar
@@ -2317,7 +2317,7 @@ unused devices: <none>
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/meminfo
-Lines: 42
+Lines: 43
 MemTotal:       15666184 kB
 MemFree:          440324 kB
 Buffers:         1020128 kB
@@ -2351,6 +2351,7 @@ Committed_AS:     530844 kB
 VmallocTotal:   34359738367 kB
 VmallocUsed:       36596 kB
 VmallocChunk:   34359637840 kB
+Percpu:            26176 kB
 HardwareCorrupted:     0 kB
 AnonHugePages:     12288 kB
 HugePages_Total:       0