1// Copyright 2017 The Prometheus Authors 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14package xfs 15 16import ( 17 "bufio" 18 "fmt" 19 "io" 20 "strings" 21 22 "github.com/prometheus/procfs/internal/util" 23) 24 25// ParseStats parses a Stats from an input io.Reader, using the format 26// found in /proc/fs/xfs/stat. 27func ParseStats(r io.Reader) (*Stats, error) { 28 const ( 29 // Fields parsed into stats structures. 30 fieldExtentAlloc = "extent_alloc" 31 fieldAbt = "abt" 32 fieldBlkMap = "blk_map" 33 fieldBmbt = "bmbt" 34 fieldDir = "dir" 35 fieldTrans = "trans" 36 fieldIg = "ig" 37 fieldLog = "log" 38 fieldRw = "rw" 39 fieldAttr = "attr" 40 fieldIcluster = "icluster" 41 fieldVnodes = "vnodes" 42 fieldBuf = "buf" 43 fieldXpc = "xpc" 44 45 // Unimplemented at this time due to lack of documentation. 46 fieldPushAil = "push_ail" 47 fieldXstrat = "xstrat" 48 fieldAbtb2 = "abtb2" 49 fieldAbtc2 = "abtc2" 50 fieldBmbt2 = "bmbt2" 51 fieldIbt2 = "ibt2" 52 fieldFibt2 = "fibt2" 53 fieldQm = "qm" 54 fieldDebug = "debug" 55 ) 56 57 var xfss Stats 58 59 s := bufio.NewScanner(r) 60 for s.Scan() { 61 // Expect at least a string label and a single integer value, ex: 62 // - abt 0 63 // - rw 1 2 64 ss := strings.Fields(string(s.Bytes())) 65 if len(ss) < 2 { 66 continue 67 } 68 label := ss[0] 69 70 // Extended precision counters are uint64 values. 71 if label == fieldXpc { 72 us, err := util.ParseUint64s(ss[1:]) 73 if err != nil { 74 return nil, err 75 } 76 77 xfss.ExtendedPrecision, err = extendedPrecisionStats(us) 78 if err != nil { 79 return nil, err 80 } 81 82 continue 83 } 84 85 // All other counters are uint32 values. 86 us, err := util.ParseUint32s(ss[1:]) 87 if err != nil { 88 return nil, err 89 } 90 91 switch label { 92 case fieldExtentAlloc: 93 xfss.ExtentAllocation, err = extentAllocationStats(us) 94 case fieldAbt: 95 xfss.AllocationBTree, err = btreeStats(us) 96 case fieldBlkMap: 97 xfss.BlockMapping, err = blockMappingStats(us) 98 case fieldBmbt: 99 xfss.BlockMapBTree, err = btreeStats(us) 100 case fieldDir: 101 xfss.DirectoryOperation, err = directoryOperationStats(us) 102 case fieldTrans: 103 xfss.Transaction, err = transactionStats(us) 104 case fieldIg: 105 xfss.InodeOperation, err = inodeOperationStats(us) 106 case fieldLog: 107 xfss.LogOperation, err = logOperationStats(us) 108 case fieldRw: 109 xfss.ReadWrite, err = readWriteStats(us) 110 case fieldAttr: 111 xfss.AttributeOperation, err = attributeOperationStats(us) 112 case fieldIcluster: 113 xfss.InodeClustering, err = inodeClusteringStats(us) 114 case fieldVnodes: 115 xfss.Vnode, err = vnodeStats(us) 116 case fieldBuf: 117 xfss.Buffer, err = bufferStats(us) 118 } 119 if err != nil { 120 return nil, err 121 } 122 } 123 124 return &xfss, s.Err() 125} 126 127// extentAllocationStats builds an ExtentAllocationStats from a slice of uint32s. 128func extentAllocationStats(us []uint32) (ExtentAllocationStats, error) { 129 if l := len(us); l != 4 { 130 return ExtentAllocationStats{}, fmt.Errorf("incorrect number of values for XFS extent allocation stats: %d", l) 131 } 132 133 return ExtentAllocationStats{ 134 ExtentsAllocated: us[0], 135 BlocksAllocated: us[1], 136 ExtentsFreed: us[2], 137 BlocksFreed: us[3], 138 }, nil 139} 140 141// btreeStats builds a BTreeStats from a slice of uint32s. 142func btreeStats(us []uint32) (BTreeStats, error) { 143 if l := len(us); l != 4 { 144 return BTreeStats{}, fmt.Errorf("incorrect number of values for XFS btree stats: %d", l) 145 } 146 147 return BTreeStats{ 148 Lookups: us[0], 149 Compares: us[1], 150 RecordsInserted: us[2], 151 RecordsDeleted: us[3], 152 }, nil 153} 154 155// BlockMappingStat builds a BlockMappingStats from a slice of uint32s. 156func blockMappingStats(us []uint32) (BlockMappingStats, error) { 157 if l := len(us); l != 7 { 158 return BlockMappingStats{}, fmt.Errorf("incorrect number of values for XFS block mapping stats: %d", l) 159 } 160 161 return BlockMappingStats{ 162 Reads: us[0], 163 Writes: us[1], 164 Unmaps: us[2], 165 ExtentListInsertions: us[3], 166 ExtentListDeletions: us[4], 167 ExtentListLookups: us[5], 168 ExtentListCompares: us[6], 169 }, nil 170} 171 172// DirectoryOperationStats builds a DirectoryOperationStats from a slice of uint32s. 173func directoryOperationStats(us []uint32) (DirectoryOperationStats, error) { 174 if l := len(us); l != 4 { 175 return DirectoryOperationStats{}, fmt.Errorf("incorrect number of values for XFS directory operation stats: %d", l) 176 } 177 178 return DirectoryOperationStats{ 179 Lookups: us[0], 180 Creates: us[1], 181 Removes: us[2], 182 Getdents: us[3], 183 }, nil 184} 185 186// TransactionStats builds a TransactionStats from a slice of uint32s. 187func transactionStats(us []uint32) (TransactionStats, error) { 188 if l := len(us); l != 3 { 189 return TransactionStats{}, fmt.Errorf("incorrect number of values for XFS transaction stats: %d", l) 190 } 191 192 return TransactionStats{ 193 Sync: us[0], 194 Async: us[1], 195 Empty: us[2], 196 }, nil 197} 198 199// InodeOperationStats builds an InodeOperationStats from a slice of uint32s. 200func inodeOperationStats(us []uint32) (InodeOperationStats, error) { 201 if l := len(us); l != 7 { 202 return InodeOperationStats{}, fmt.Errorf("incorrect number of values for XFS inode operation stats: %d", l) 203 } 204 205 return InodeOperationStats{ 206 Attempts: us[0], 207 Found: us[1], 208 Recycle: us[2], 209 Missed: us[3], 210 Duplicate: us[4], 211 Reclaims: us[5], 212 AttributeChange: us[6], 213 }, nil 214} 215 216// LogOperationStats builds a LogOperationStats from a slice of uint32s. 217func logOperationStats(us []uint32) (LogOperationStats, error) { 218 if l := len(us); l != 5 { 219 return LogOperationStats{}, fmt.Errorf("incorrect number of values for XFS log operation stats: %d", l) 220 } 221 222 return LogOperationStats{ 223 Writes: us[0], 224 Blocks: us[1], 225 NoInternalBuffers: us[2], 226 Force: us[3], 227 ForceSleep: us[4], 228 }, nil 229} 230 231// ReadWriteStats builds a ReadWriteStats from a slice of uint32s. 232func readWriteStats(us []uint32) (ReadWriteStats, error) { 233 if l := len(us); l != 2 { 234 return ReadWriteStats{}, fmt.Errorf("incorrect number of values for XFS read write stats: %d", l) 235 } 236 237 return ReadWriteStats{ 238 Read: us[0], 239 Write: us[1], 240 }, nil 241} 242 243// AttributeOperationStats builds an AttributeOperationStats from a slice of uint32s. 244func attributeOperationStats(us []uint32) (AttributeOperationStats, error) { 245 if l := len(us); l != 4 { 246 return AttributeOperationStats{}, fmt.Errorf("incorrect number of values for XFS attribute operation stats: %d", l) 247 } 248 249 return AttributeOperationStats{ 250 Get: us[0], 251 Set: us[1], 252 Remove: us[2], 253 List: us[3], 254 }, nil 255} 256 257// InodeClusteringStats builds an InodeClusteringStats from a slice of uint32s. 258func inodeClusteringStats(us []uint32) (InodeClusteringStats, error) { 259 if l := len(us); l != 3 { 260 return InodeClusteringStats{}, fmt.Errorf("incorrect number of values for XFS inode clustering stats: %d", l) 261 } 262 263 return InodeClusteringStats{ 264 Iflush: us[0], 265 Flush: us[1], 266 FlushInode: us[2], 267 }, nil 268} 269 270// VnodeStats builds a VnodeStats from a slice of uint32s. 271func vnodeStats(us []uint32) (VnodeStats, error) { 272 // The attribute "Free" appears to not be available on older XFS 273 // stats versions. Therefore, 7 or 8 elements may appear in 274 // this slice. 275 l := len(us) 276 if l != 7 && l != 8 { 277 return VnodeStats{}, fmt.Errorf("incorrect number of values for XFS vnode stats: %d", l) 278 } 279 280 s := VnodeStats{ 281 Active: us[0], 282 Allocate: us[1], 283 Get: us[2], 284 Hold: us[3], 285 Release: us[4], 286 Reclaim: us[5], 287 Remove: us[6], 288 } 289 290 // Skip adding free, unless it is present. The zero value will 291 // be used in place of an actual count. 292 if l == 7 { 293 return s, nil 294 } 295 296 s.Free = us[7] 297 return s, nil 298} 299 300// BufferStats builds a BufferStats from a slice of uint32s. 301func bufferStats(us []uint32) (BufferStats, error) { 302 if l := len(us); l != 9 { 303 return BufferStats{}, fmt.Errorf("incorrect number of values for XFS buffer stats: %d", l) 304 } 305 306 return BufferStats{ 307 Get: us[0], 308 Create: us[1], 309 GetLocked: us[2], 310 GetLockedWaited: us[3], 311 BusyLocked: us[4], 312 MissLocked: us[5], 313 PageRetries: us[6], 314 PageFound: us[7], 315 GetRead: us[8], 316 }, nil 317} 318 319// ExtendedPrecisionStats builds an ExtendedPrecisionStats from a slice of uint32s. 320func extendedPrecisionStats(us []uint64) (ExtendedPrecisionStats, error) { 321 if l := len(us); l != 3 { 322 return ExtendedPrecisionStats{}, fmt.Errorf("incorrect number of values for XFS extended precision stats: %d", l) 323 } 324 325 return ExtendedPrecisionStats{ 326 FlushBytes: us[0], 327 WriteBytes: us[1], 328 ReadBytes: us[2], 329 }, nil 330} 331