Skip to content

Commit

Permalink
Add Disk IO stats and ext4 FS stats (prometheus#651)
Browse files Browse the repository at this point in the history
Adds function to return disk stats from:
* /sys/block/<disk>/device/ioerr_cnt: number of SCSI commands that completed with an error
* /sys/block/<disk>/device/iodone_cnt: number of completed or rejected SCSI commands

Add function to return stats for ext4 filesystem
* /sys/fs/ext4/<partition>/errors_count: number of ext4 errors
* /sys/fs/ext4/<partition>/warning_count: number of ext4 warning log messages
* /sys/fs/ext4/<partition>/msg_count: number of other ext4 log messages

Signed-off-by: Muhammad Shahzeb <[email protected]>
Signed-off-by: fs185143 <[email protected]>
  • Loading branch information
mshahzeb authored and fs185143 committed Nov 26, 2024
1 parent a7e955a commit b794543
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
28 changes: 28 additions & 0 deletions blockdevice/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -210,6 +215,7 @@ const (
sysBlockDM = "dm"
sysUnderlyingDev = "slaves"
sysBlockSize = "size"
sysDevicePath = "device"
)

// FS represents the pseudo-filesystems proc and sys, which provides an
Expand Down Expand Up @@ -485,3 +491,25 @@ func (fs FS) SysBlockDeviceSize(device string) (uint64, error) {
}
return filesystem.SectorSize * size, nil
}

// SysBlockDeviceIO returns stats for the block device io counters
// IO done count: /sys/block/<disk>/device/iodone_cnt
// IO error count: /sys/block/<disk>/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
}
103 changes: 103 additions & 0 deletions ext4/ext4.go
Original file line number Diff line number Diff line change
@@ -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
}
14 changes: 14 additions & 0 deletions internal/util/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package util

import (
"errors"
"os"
"strconv"
"strings"
Expand Down Expand Up @@ -110,3 +111,16 @@ 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
}
hexString := strings.TrimSpace(string(data))
if !strings.HasPrefix(hexString, "0x") {
return 0, errors.New("invalid format: hex string does not start with '0x'")
}
return strconv.ParseUint(hexString[2:], 16, 64)
}

0 comments on commit b794543

Please sign in to comment.