parent
d354971e81
commit
5c6ff86f9d
@ -0,0 +1,95 @@ |
||||
// Copyright 2017 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" |
||||
"fmt" |
||||
"io" |
||||
"os" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
// A BuddyInfo is the details parsed from /proc/buddyinfo.
|
||||
// The data is comprised of an array of free fragments of each size.
|
||||
// The sizes are 2^n*PAGE_SIZE, where n is the array index.
|
||||
type BuddyInfo struct { |
||||
Node string |
||||
Zone string |
||||
Sizes []float64 |
||||
} |
||||
|
||||
// NewBuddyInfo reads the buddyinfo statistics.
|
||||
func NewBuddyInfo() ([]BuddyInfo, error) { |
||||
fs, err := NewFS(DefaultMountPoint) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return fs.NewBuddyInfo() |
||||
} |
||||
|
||||
// NewBuddyInfo reads the buddyinfo statistics from the specified `proc` filesystem.
|
||||
func (fs FS) NewBuddyInfo() ([]BuddyInfo, error) { |
||||
file, err := os.Open(fs.Path("buddyinfo")) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer file.Close() |
||||
|
||||
return parseBuddyInfo(file) |
||||
} |
||||
|
||||
func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) { |
||||
var ( |
||||
buddyInfo = []BuddyInfo{} |
||||
scanner = bufio.NewScanner(r) |
||||
bucketCount = -1 |
||||
) |
||||
|
||||
for scanner.Scan() { |
||||
var err error |
||||
line := scanner.Text() |
||||
parts := strings.Fields(string(line)) |
||||
|
||||
if len(parts) < 4 { |
||||
return nil, fmt.Errorf("invalid number of fields when parsing buddyinfo") |
||||
} |
||||
|
||||
node := strings.TrimRight(parts[1], ",") |
||||
zone := strings.TrimRight(parts[3], ",") |
||||
arraySize := len(parts[4:]) |
||||
|
||||
if bucketCount == -1 { |
||||
bucketCount = arraySize |
||||
} else { |
||||
if bucketCount != arraySize { |
||||
return nil, fmt.Errorf("mismatch in number of buddyinfo buckets, previous count %d, new count %d", bucketCount, arraySize) |
||||
} |
||||
} |
||||
|
||||
sizes := make([]float64, arraySize) |
||||
for i := 0; i < arraySize; i++ { |
||||
sizes[i], err = strconv.ParseFloat(parts[i+4], 64) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("invalid value in buddyinfo: %s", err) |
||||
} |
||||
} |
||||
|
||||
buddyInfo = append(buddyInfo, BuddyInfo{node, zone, sizes}) |
||||
} |
||||
|
||||
return buddyInfo, scanner.Err() |
||||
} |
||||
@ -0,0 +1,361 @@ |
||||
// Copyright 2017 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 xfs |
||||
|
||||
import ( |
||||
"bufio" |
||||
"fmt" |
||||
"io" |
||||
"log" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
// ParseStats parses a Stats from an input io.Reader, using the format
|
||||
// found in /proc/fs/xfs/stat.
|
||||
func ParseStats(r io.Reader) (*Stats, error) { |
||||
const ( |
||||
// Fields parsed into stats structures.
|
||||
fieldExtentAlloc = "extent_alloc" |
||||
fieldAbt = "abt" |
||||
fieldBlkMap = "blk_map" |
||||
fieldBmbt = "bmbt" |
||||
fieldDir = "dir" |
||||
fieldTrans = "trans" |
||||
fieldIg = "ig" |
||||
fieldLog = "log" |
||||
fieldRw = "rw" |
||||
fieldAttr = "attr" |
||||
fieldIcluster = "icluster" |
||||
fieldVnodes = "vnodes" |
||||
fieldBuf = "buf" |
||||
fieldXpc = "xpc" |
||||
|
||||
// Unimplemented at this time due to lack of documentation.
|
||||
fieldPushAil = "push_ail" |
||||
fieldXstrat = "xstrat" |
||||
fieldAbtb2 = "abtb2" |
||||
fieldAbtc2 = "abtc2" |
||||
fieldBmbt2 = "bmbt2" |
||||
fieldIbt2 = "ibt2" |
||||
fieldFibt2 = "fibt2" |
||||
fieldQm = "qm" |
||||
fieldDebug = "debug" |
||||
) |
||||
|
||||
var xfss Stats |
||||
|
||||
s := bufio.NewScanner(r) |
||||
for s.Scan() { |
||||
// Expect at least a string label and a single integer value, ex:
|
||||
// - abt 0
|
||||
// - rw 1 2
|
||||
ss := strings.Fields(string(s.Bytes())) |
||||
if len(ss) < 2 { |
||||
continue |
||||
} |
||||
label := ss[0] |
||||
|
||||
// Extended precision counters are uint64 values.
|
||||
if label == fieldXpc { |
||||
us, err := parseUint64s(ss[1:]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
xfss.ExtendedPrecision, err = extendedPrecisionStats(us) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
continue |
||||
} |
||||
|
||||
// All other counters are uint32 values.
|
||||
us, err := parseUint32s(ss[1:]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
switch label { |
||||
case fieldExtentAlloc: |
||||
xfss.ExtentAllocation, err = extentAllocationStats(us) |
||||
case fieldAbt: |
||||
xfss.AllocationBTree, err = btreeStats(us) |
||||
case fieldBlkMap: |
||||
xfss.BlockMapping, err = blockMappingStats(us) |
||||
case fieldBmbt: |
||||
xfss.BlockMapBTree, err = btreeStats(us) |
||||
case fieldDir: |
||||
xfss.DirectoryOperation, err = directoryOperationStats(us) |
||||
case fieldTrans: |
||||
xfss.Transaction, err = transactionStats(us) |
||||
case fieldIg: |
||||
xfss.InodeOperation, err = inodeOperationStats(us) |
||||
case fieldLog: |
||||
xfss.LogOperation, err = logOperationStats(us) |
||||
case fieldRw: |
||||
xfss.ReadWrite, err = readWriteStats(us) |
||||
case fieldAttr: |
||||
xfss.AttributeOperation, err = attributeOperationStats(us) |
||||
case fieldIcluster: |
||||
xfss.InodeClustering, err = inodeClusteringStats(us) |
||||
case fieldVnodes: |
||||
xfss.Vnode, err = vnodeStats(us) |
||||
case fieldBuf: |
||||
xfss.Buffer, err = bufferStats(us) |
||||
} |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
|
||||
return &xfss, s.Err() |
||||
} |
||||
|
||||
// extentAllocationStats builds an ExtentAllocationStats from a slice of uint32s.
|
||||
func extentAllocationStats(us []uint32) (ExtentAllocationStats, error) { |
||||
if l := len(us); l != 4 { |
||||
return ExtentAllocationStats{}, fmt.Errorf("incorrect number of values for XFS extent allocation stats: %d", l) |
||||
} |
||||
|
||||
return ExtentAllocationStats{ |
||||
ExtentsAllocated: us[0], |
||||
BlocksAllocated: us[1], |
||||
ExtentsFreed: us[2], |
||||
BlocksFreed: us[3], |
||||
}, nil |
||||
} |
||||
|
||||
// btreeStats builds a BTreeStats from a slice of uint32s.
|
||||
func btreeStats(us []uint32) (BTreeStats, error) { |
||||
if l := len(us); l != 4 { |
||||
return BTreeStats{}, fmt.Errorf("incorrect number of values for XFS btree stats: %d", l) |
||||
} |
||||
|
||||
return BTreeStats{ |
||||
Lookups: us[0], |
||||
Compares: us[1], |
||||
RecordsInserted: us[2], |
||||
RecordsDeleted: us[3], |
||||
}, nil |
||||
} |
||||
|
||||
// BlockMappingStat builds a BlockMappingStats from a slice of uint32s.
|
||||
func blockMappingStats(us []uint32) (BlockMappingStats, error) { |
||||
if l := len(us); l != 7 { |
||||
return BlockMappingStats{}, fmt.Errorf("incorrect number of values for XFS block mapping stats: %d", l) |
||||
} |
||||
|
||||
return BlockMappingStats{ |
||||
Reads: us[0], |
||||
Writes: us[1], |
||||
Unmaps: us[2], |
||||
ExtentListInsertions: us[3], |
||||
ExtentListDeletions: us[4], |
||||
ExtentListLookups: us[5], |
||||
ExtentListCompares: us[6], |
||||
}, nil |
||||
} |
||||
|
||||
// DirectoryOperationStats builds a DirectoryOperationStats from a slice of uint32s.
|
||||
func directoryOperationStats(us []uint32) (DirectoryOperationStats, error) { |
||||
if l := len(us); l != 4 { |
||||
return DirectoryOperationStats{}, fmt.Errorf("incorrect number of values for XFS directory operation stats: %d", l) |
||||
} |
||||
|
||||
return DirectoryOperationStats{ |
||||
Lookups: us[0], |
||||
Creates: us[1], |
||||
Removes: us[2], |
||||
Getdents: us[3], |
||||
}, nil |
||||
} |
||||
|
||||
// TransactionStats builds a TransactionStats from a slice of uint32s.
|
||||
func transactionStats(us []uint32) (TransactionStats, error) { |
||||
if l := len(us); l != 3 { |
||||
return TransactionStats{}, fmt.Errorf("incorrect number of values for XFS transaction stats: %d", l) |
||||
} |
||||
|
||||
return TransactionStats{ |
||||
Sync: us[0], |
||||
Async: us[1], |
||||
Empty: us[2], |
||||
}, nil |
||||
} |
||||
|
||||
// InodeOperationStats builds an InodeOperationStats from a slice of uint32s.
|
||||
func inodeOperationStats(us []uint32) (InodeOperationStats, error) { |
||||
if l := len(us); l != 7 { |
||||
return InodeOperationStats{}, fmt.Errorf("incorrect number of values for XFS inode operation stats: %d", l) |
||||
} |
||||
|
||||
return InodeOperationStats{ |
||||
Attempts: us[0], |
||||
Found: us[1], |
||||
Recycle: us[2], |
||||
Missed: us[3], |
||||
Duplicate: us[4], |
||||
Reclaims: us[5], |
||||
AttributeChange: us[6], |
||||
}, nil |
||||
} |
||||
|
||||
// LogOperationStats builds a LogOperationStats from a slice of uint32s.
|
||||
func logOperationStats(us []uint32) (LogOperationStats, error) { |
||||
if l := len(us); l != 5 { |
||||
return LogOperationStats{}, fmt.Errorf("incorrect number of values for XFS log operation stats: %d", l) |
||||
} |
||||
|
||||
return LogOperationStats{ |
||||
Writes: us[0], |
||||
Blocks: us[1], |
||||
NoInternalBuffers: us[2], |
||||
Force: us[3], |
||||
ForceSleep: us[4], |
||||
}, nil |
||||
} |
||||
|
||||
// ReadWriteStats builds a ReadWriteStats from a slice of uint32s.
|
||||
func readWriteStats(us []uint32) (ReadWriteStats, error) { |
||||
if l := len(us); l != 2 { |
||||
return ReadWriteStats{}, fmt.Errorf("incorrect number of values for XFS read write stats: %d", l) |
||||
} |
||||
|
||||
return ReadWriteStats{ |
||||
Read: us[0], |
||||
Write: us[1], |
||||
}, nil |
||||
} |
||||
|
||||
// AttributeOperationStats builds an AttributeOperationStats from a slice of uint32s.
|
||||
func attributeOperationStats(us []uint32) (AttributeOperationStats, error) { |
||||
if l := len(us); l != 4 { |
||||
return AttributeOperationStats{}, fmt.Errorf("incorrect number of values for XFS attribute operation stats: %d", l) |
||||
} |
||||
|
||||
return AttributeOperationStats{ |
||||
Get: us[0], |
||||
Set: us[1], |
||||
Remove: us[2], |
||||
List: us[3], |
||||
}, nil |
||||
} |
||||
|
||||
// InodeClusteringStats builds an InodeClusteringStats from a slice of uint32s.
|
||||
func inodeClusteringStats(us []uint32) (InodeClusteringStats, error) { |
||||
if l := len(us); l != 3 { |
||||
return InodeClusteringStats{}, fmt.Errorf("incorrect number of values for XFS inode clustering stats: %d", l) |
||||
} |
||||
|
||||
return InodeClusteringStats{ |
||||
Iflush: us[0], |
||||
Flush: us[1], |
||||
FlushInode: us[2], |
||||
}, nil |
||||
} |
||||
|
||||
// VnodeStats builds a VnodeStats from a slice of uint32s.
|
||||
func vnodeStats(us []uint32) (VnodeStats, error) { |
||||
// The attribute "Free" appears to not be available on older XFS
|
||||
// stats versions. Therefore, 7 or 8 elements may appear in
|
||||
// this slice.
|
||||
l := len(us) |
||||
log.Println(l) |
||||
if l != 7 && l != 8 { |
||||
return VnodeStats{}, fmt.Errorf("incorrect number of values for XFS vnode stats: %d", l) |
||||
} |
||||
|
||||
s := VnodeStats{ |
||||
Active: us[0], |
||||
Allocate: us[1], |
||||
Get: us[2], |
||||
Hold: us[3], |
||||
Release: us[4], |
||||
Reclaim: us[5], |
||||
Remove: us[6], |
||||
} |
||||
|
||||
// Skip adding free, unless it is present. The zero value will
|
||||
// be used in place of an actual count.
|
||||
if l == 7 { |
||||
return s, nil |
||||
} |
||||
|
||||
s.Free = us[7] |
||||
return s, nil |
||||
} |
||||
|
||||
// BufferStats builds a BufferStats from a slice of uint32s.
|
||||
func bufferStats(us []uint32) (BufferStats, error) { |
||||
if l := len(us); l != 9 { |
||||
return BufferStats{}, fmt.Errorf("incorrect number of values for XFS buffer stats: %d", l) |
||||
} |
||||
|
||||
return BufferStats{ |
||||
Get: us[0], |
||||
Create: us[1], |
||||
GetLocked: us[2], |
||||
GetLockedWaited: us[3], |
||||
BusyLocked: us[4], |
||||
MissLocked: us[5], |
||||
PageRetries: us[6], |
||||
PageFound: us[7], |
||||
GetRead: us[8], |
||||
}, nil |
||||
} |
||||
|
||||
// ExtendedPrecisionStats builds an ExtendedPrecisionStats from a slice of uint32s.
|
||||
func extendedPrecisionStats(us []uint64) (ExtendedPrecisionStats, error) { |
||||
if l := len(us); l != 3 { |
||||
return ExtendedPrecisionStats{}, fmt.Errorf("incorrect number of values for XFS extended precision stats: %d", l) |
||||
} |
||||
|
||||
return ExtendedPrecisionStats{ |
||||
FlushBytes: us[0], |
||||
WriteBytes: us[1], |
||||
ReadBytes: us[2], |
||||
}, nil |
||||
} |
||||
|
||||
// parseUint32s parses a slice of strings into a slice of uint32s.
|
||||
func parseUint32s(ss []string) ([]uint32, error) { |
||||
us := make([]uint32, 0, len(ss)) |
||||
for _, s := range ss { |
||||
u, err := strconv.ParseUint(s, 10, 32) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
us = append(us, uint32(u)) |
||||
} |
||||
|
||||
return us, nil |
||||
} |
||||
|
||||
// parseUint64s parses a slice of strings into a slice of uint64s.
|
||||
func parseUint64s(ss []string) ([]uint64, error) { |
||||
us := make([]uint64, 0, len(ss)) |
||||
for _, s := range ss { |
||||
u, err := strconv.ParseUint(s, 10, 64) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
us = append(us, u) |
||||
} |
||||
|
||||
return us, nil |
||||
} |
||||
@ -0,0 +1,158 @@ |
||||
// Copyright 2017 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 xfs provides access to statistics exposed by the XFS filesystem.
|
||||
package xfs |
||||
|
||||
// Stats contains XFS filesystem runtime statistics, parsed from
|
||||
// /proc/fs/xfs/stat.
|
||||
//
|
||||
// The names and meanings of each statistic were taken from
|
||||
// http://xfs.org/index.php/Runtime_Stats and xfs_stats.h in the Linux
|
||||
// kernel source. Most counters are uint32s (same data types used in
|
||||
// xfs_stats.h), but some of the "extended precision stats" are uint64s.
|
||||
type Stats struct { |
||||
ExtentAllocation ExtentAllocationStats |
||||
AllocationBTree BTreeStats |
||||
BlockMapping BlockMappingStats |
||||
BlockMapBTree BTreeStats |
||||
DirectoryOperation DirectoryOperationStats |
||||
Transaction TransactionStats |
||||
InodeOperation InodeOperationStats |
||||
LogOperation LogOperationStats |
||||
ReadWrite ReadWriteStats |
||||
AttributeOperation AttributeOperationStats |
||||
InodeClustering InodeClusteringStats |
||||
Vnode VnodeStats |
||||
Buffer BufferStats |
||||
ExtendedPrecision ExtendedPrecisionStats |
||||
} |
||||
|
||||
// ExtentAllocationStats contains statistics regarding XFS extent allocations.
|
||||
type ExtentAllocationStats struct { |
||||
ExtentsAllocated uint32 |
||||
BlocksAllocated uint32 |
||||
ExtentsFreed uint32 |
||||
BlocksFreed uint32 |
||||
} |
||||
|
||||
// BTreeStats contains statistics regarding an XFS internal B-tree.
|
||||
type BTreeStats struct { |
||||
Lookups uint32 |
||||
Compares uint32 |
||||
RecordsInserted uint32 |
||||
RecordsDeleted uint32 |
||||
} |
||||
|
||||
// BlockMappingStats contains statistics regarding XFS block maps.
|
||||
type BlockMappingStats struct { |
||||
Reads uint32 |
||||
Writes uint32 |
||||
Unmaps uint32 |
||||
ExtentListInsertions uint32 |
||||
ExtentListDeletions uint32 |
||||
ExtentListLookups uint32 |
||||
ExtentListCompares uint32 |
||||
} |
||||
|
||||
// DirectoryOperationStats contains statistics regarding XFS directory entries.
|
||||
type DirectoryOperationStats struct { |
||||
Lookups uint32 |
||||
Creates uint32 |
||||
Removes uint32 |
||||
Getdents uint32 |
||||
} |
||||
|
||||
// TransactionStats contains statistics regarding XFS metadata transactions.
|
||||
type TransactionStats struct { |
||||
Sync uint32 |
||||
Async uint32 |
||||
Empty uint32 |
||||
} |
||||
|
||||
// InodeOperationStats contains statistics regarding XFS inode operations.
|
||||
type InodeOperationStats struct { |
||||
Attempts uint32 |
||||
Found uint32 |
||||
Recycle uint32 |
||||
Missed uint32 |
||||
Duplicate uint32 |
||||
Reclaims uint32 |
||||
AttributeChange uint32 |
||||
} |
||||
|
||||
// LogOperationStats contains statistics regarding the XFS log buffer.
|
||||
type LogOperationStats struct { |
||||
Writes uint32 |
||||
Blocks uint32 |
||||
NoInternalBuffers uint32 |
||||
Force uint32 |
||||
ForceSleep uint32 |
||||
} |
||||
|
||||
// ReadWriteStats contains statistics regarding the number of read and write
|
||||
// system calls for XFS filesystems.
|
||||
type ReadWriteStats struct { |
||||
Read uint32 |
||||
Write uint32 |
||||
} |
||||
|
||||
// AttributeOperationStats contains statistics regarding manipulation of
|
||||
// XFS extended file attributes.
|
||||
type AttributeOperationStats struct { |
||||
Get uint32 |
||||
Set uint32 |
||||
Remove uint32 |
||||
List uint32 |
||||
} |
||||
|
||||
// InodeClusteringStats contains statistics regarding XFS inode clustering
|
||||
// operations.
|
||||
type InodeClusteringStats struct { |
||||
Iflush uint32 |
||||
Flush uint32 |
||||
FlushInode uint32 |
||||
} |
||||
|
||||
// VnodeStats contains statistics regarding XFS vnode operations.
|
||||
type VnodeStats struct { |
||||
Active uint32 |
||||
Allocate uint32 |
||||
Get uint32 |
||||
Hold uint32 |
||||
Release uint32 |
||||
Reclaim uint32 |
||||
Remove uint32 |
||||
Free uint32 |
||||
} |
||||
|
||||
// BufferStats contains statistics regarding XFS read/write I/O buffers.
|
||||
type BufferStats struct { |
||||
Get uint32 |
||||
Create uint32 |
||||
GetLocked uint32 |
||||
GetLockedWaited uint32 |
||||
BusyLocked uint32 |
||||
MissLocked uint32 |
||||
PageRetries uint32 |
||||
PageFound uint32 |
||||
GetRead uint32 |
||||
} |
||||
|
||||
// ExtendedPrecisionStats contains high precision counters used to track the
|
||||
// total number of bytes read, written, or flushed, during XFS operations.
|
||||
type ExtendedPrecisionStats struct { |
||||
FlushBytes uint64 |
||||
WriteBytes uint64 |
||||
ReadBytes uint64 |
||||
} |
||||
Loading…
Reference in new issue