From afabf5c6a12ccf5be2037e4dfb4930c8125f69bd Mon Sep 17 00:00:00 2001 From: Muhammad Shahzeb Date: Tue, 16 Jul 2024 23:32:54 +0500 Subject: [PATCH] Squashing commits Signed-off-by: Muhammad Shahzeb --- .github/workflows/golangci-lint.yml | 2 +- blockdevice/stats.go | 28 ++++++++ ext4/ext4.go | 103 ++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- internal/util/parse.go | 9 +++ proc_status.go | 18 +++-- proc_status_test.go | 16 +++++ testdata/fixtures.ttar | 8 +-- 9 files changed, 175 insertions(+), 15 deletions(-) create mode 100644 ext4/ext4.go diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 5ceb59099..8de7af639 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -26,7 +26,7 @@ jobs: - name: Checkout repository uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Install Go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version: 1.22.x - name: Install snmp_exporter/generator dependencies diff --git a/blockdevice/stats.go b/blockdevice/stats.go index 22533002d..75b68845a 100644 --- a/blockdevice/stats.go +++ b/blockdevice/stats.go @@ -178,6 +178,11 @@ type BlockQueueStats struct { WriteZeroesMaxBytes uint64 } +type IODeviceStats struct { + IODoneCount uint64 + IOErrCount uint64 +} + // DeviceMapperInfo models the devicemapper files that are located in the sysfs tree for each block device // and described in the kernel documentation: // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm @@ -209,6 +214,7 @@ const ( sysBlockQueue = "queue" sysBlockDM = "dm" sysUnderlyingDev = "slaves" + sysDevicePath = "device" ) // FS represents the pseudo-filesystems proc and sys, which provides an @@ -474,3 +480,25 @@ func (fs FS) SysBlockDeviceUnderlyingDevices(device string) (UnderlyingDeviceInf return UnderlyingDeviceInfo{DeviceNames: underlying}, nil } + +// SysBlockDeviceIO returns stats for the block device io counters +// IO done count: /sys/block//device/iodone_cnt +// IO error count: /sys/block//device/ioerr_cnt. +func (fs FS) SysBlockDeviceIOStat(device string) (IODeviceStats, error) { + var ( + ioDeviceStats IODeviceStats + err error + ) + for file, p := range map[string]*uint64{ + "iodone_cnt": &ioDeviceStats.IODoneCount, + "ioerr_cnt": &ioDeviceStats.IOErrCount, + } { + var val uint64 + val, err = util.ReadHexFromFile(fs.sys.Path(sysBlockPath, device, sysDevicePath, file)) + if err != nil { + return IODeviceStats{}, err + } + *p = val + } + return ioDeviceStats, nil +} diff --git a/ext4/ext4.go b/ext4/ext4.go new file mode 100644 index 000000000..b7f83a583 --- /dev/null +++ b/ext4/ext4.go @@ -0,0 +1,103 @@ +// 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 btrfs provides access to statistics exposed by ext4 filesystems. +package ext4 + +import ( + "path/filepath" + "strings" + + "github.com/prometheus/procfs/internal/fs" + "github.com/prometheus/procfs/internal/util" +) + +const ( + sysFSPath = "fs" + sysFSExt4Path = "ext4" +) + +// Stats contains statistics for a single Btrfs filesystem. +// See Linux fs/btrfs/sysfs.c for more information. +type Stats struct { + Name string + + Errors uint64 + Warnings uint64 + Messages uint64 +} + +// FS represents the pseudo-filesystems proc and sys, which provides an +// interface to kernel data structures. +type FS struct { + proc *fs.FS + sys *fs.FS +} + +// NewDefaultFS returns a new blockdevice fs using the default mountPoints for proc and sys. +// It will error if either of these mount points can't be read. +func NewDefaultFS() (FS, error) { + return NewFS(fs.DefaultProcMountPoint, fs.DefaultSysMountPoint) +} + +// NewFS returns a new XFS handle using the given proc and sys mountPoints. It will error +// if either of the mounts point can't be read. +func NewFS(procMountPoint string, sysMountPoint string) (FS, error) { + if strings.TrimSpace(procMountPoint) == "" { + procMountPoint = fs.DefaultProcMountPoint + } + procfs, err := fs.NewFS(procMountPoint) + if err != nil { + return FS{}, err + } + if strings.TrimSpace(sysMountPoint) == "" { + sysMountPoint = fs.DefaultSysMountPoint + } + sysfs, err := fs.NewFS(sysMountPoint) + if err != nil { + return FS{}, err + } + return FS{&procfs, &sysfs}, nil +} + +// ProcStat returns stats for the filesystem. +func (fs FS) ProcStat() ([]*Stats, error) { + matches, err := filepath.Glob(fs.sys.Path("fs/ext4/*")) + if err != nil { + return nil, err + } + + stats := make([]*Stats, 0, len(matches)) + for _, m := range matches { + s := &Stats{} + + // "*" used in glob above indicates the name of the filesystem. + name := filepath.Base(m) + s.Name = name + for file, p := range map[string]*uint64{ + "errors_count": &s.Errors, + "warning_count": &s.Warnings, + "msg_count": &s.Messages, + } { + var val uint64 + val, err = util.ReadUintFromFile(fs.sys.Path(sysFSPath, sysFSExt4Path, name, file)) + if err == nil { + *p = val + } + } + + stats = append(stats, s) + } + + return stats, nil +} diff --git a/go.mod b/go.mod index 6e9fd6f79..8b7c4f470 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,5 @@ go 1.20 require ( github.com/google/go-cmp v0.6.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.20.0 + golang.org/x/sys v0.21.0 ) diff --git a/go.sum b/go.sum index cc15e2fbb..535a646eb 100644 --- a/go.sum +++ b/go.sum @@ -2,5 +2,5 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/internal/util/parse.go b/internal/util/parse.go index 14272dc78..3be09c9c9 100644 --- a/internal/util/parse.go +++ b/internal/util/parse.go @@ -110,3 +110,12 @@ func ParseBool(b string) *bool { } return &truth } + +// ReadHexFromFile reads a file and attempts to parse a uint64 from a hexadecimal format 0xXX. +func ReadHexFromFile(path string) (uint64, error) { + data, err := os.ReadFile(path) + if err != nil { + return 0, err + } + return strconv.ParseUint(strings.TrimSpace(string(data[2:])), 16, 64) +} diff --git a/proc_status.go b/proc_status.go index a055197c6..dd8aa5688 100644 --- a/proc_status.go +++ b/proc_status.go @@ -146,7 +146,11 @@ func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintByt } } case "NSpid": - s.NSpids = calcNSPidsList(vString) + nspids, err := calcNSPidsList(vString) + if err != nil { + return err + } + s.NSpids = nspids case "VmPeak": s.VmPeak = vUintBytes case "VmSize": @@ -222,17 +226,17 @@ func calcCpusAllowedList(cpuString string) []uint64 { return g } -func calcNSPidsList(nspidsString string) []uint64 { - s := strings.Split(nspidsString, " ") +func calcNSPidsList(nspidsString string) ([]uint64, error) { + s := strings.Split(nspidsString, "\t") var nspids []uint64 for _, nspid := range s { - nspid, _ := strconv.ParseUint(nspid, 10, 64) - if nspid == 0 { - continue + nspid, err := strconv.ParseUint(nspid, 10, 64) + if err != nil { + return nil, err } nspids = append(nspids, nspid) } - return nspids + return nspids, nil } diff --git a/proc_status_test.go b/proc_status_test.go index 9644bf013..e41d36baf 100644 --- a/proc_status_test.go +++ b/proc_status_test.go @@ -139,3 +139,19 @@ func TestCpusAllowedList(t *testing.T) { t.Errorf("want CpusAllowedList %v, have %v", want, have) } } + +func TestNsPids(t *testing.T) { + p, err := getProcFixtures(t).Proc(26235) + if err != nil { + t.Fatal(err) + } + + s, err := p.NewStatus() + if err != nil { + t.Fatal(err) + } + + if want, have := []uint64{26235, 1}, s.NSpids; !reflect.DeepEqual(want, have) { + t.Errorf("want NsPids %v, have %v", want, have) + } +} diff --git a/testdata/fixtures.ttar b/testdata/fixtures.ttar index 4bc508b8d..f2bd5e567 100644 --- a/testdata/fixtures.ttar +++ b/testdata/fixtures.ttar @@ -818,10 +818,10 @@ Uid: 0 0 0 0 Gid: 0 0 0 0 FDSize: 64 Groups: -NStgid: 26235 1 -NSpid: 26235 1 -NSpgid: 26235 1 -NSsid: 26235 1 +NStgid: 26235 1 +NSpid: 26235 1 +NSpgid: 26235 1 +NSsid: 26235 1 VmPeak: 758200 kB VmSize: 758200 kB VmLck: 0 kB