parse.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  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. package xfs
  14. import (
  15. "bufio"
  16. "fmt"
  17. "io"
  18. "strings"
  19. "github.com/prometheus/procfs/internal/util"
  20. )
  21. // ParseStats parses a Stats from an input io.Reader, using the format
  22. // found in /proc/fs/xfs/stat.
  23. func ParseStats(r io.Reader) (*Stats, error) {
  24. const (
  25. // Fields parsed into stats structures.
  26. fieldExtentAlloc = "extent_alloc"
  27. fieldAbt = "abt"
  28. fieldBlkMap = "blk_map"
  29. fieldBmbt = "bmbt"
  30. fieldDir = "dir"
  31. fieldTrans = "trans"
  32. fieldIg = "ig"
  33. fieldLog = "log"
  34. fieldPushAil = "push_ail"
  35. fieldXstrat = "xstrat"
  36. fieldRw = "rw"
  37. fieldAttr = "attr"
  38. fieldIcluster = "icluster"
  39. fieldVnodes = "vnodes"
  40. fieldBuf = "buf"
  41. fieldXpc = "xpc"
  42. fieldAbtb2 = "abtb2"
  43. fieldAbtc2 = "abtc2"
  44. fieldBmbt2 = "bmbt2"
  45. fieldIbt2 = "ibt2"
  46. //fieldFibt2 = "fibt2"
  47. fieldQm = "qm"
  48. fieldDebug = "debug"
  49. // Unimplemented at this time due to lack of documentation.
  50. //fieldRmapbt = "rmapbt"
  51. //fieldRefcntbt = "refcntbt"
  52. )
  53. var xfss Stats
  54. s := bufio.NewScanner(r)
  55. for s.Scan() {
  56. // Expect at least a string label and a single integer value, ex:
  57. // - abt 0
  58. // - rw 1 2
  59. ss := strings.Fields(string(s.Bytes()))
  60. if len(ss) < 2 {
  61. continue
  62. }
  63. label := ss[0]
  64. // Extended precision counters are uint64 values.
  65. if label == fieldXpc {
  66. us, err := util.ParseUint64s(ss[1:])
  67. if err != nil {
  68. return nil, err
  69. }
  70. xfss.ExtendedPrecision, err = extendedPrecisionStats(us)
  71. if err != nil {
  72. return nil, err
  73. }
  74. continue
  75. }
  76. // All other counters are uint32 values.
  77. us, err := util.ParseUint32s(ss[1:])
  78. if err != nil {
  79. return nil, err
  80. }
  81. switch label {
  82. case fieldExtentAlloc:
  83. xfss.ExtentAllocation, err = extentAllocationStats(us)
  84. case fieldAbt:
  85. xfss.AllocationBTree, err = btreeStats(us)
  86. case fieldBlkMap:
  87. xfss.BlockMapping, err = blockMappingStats(us)
  88. case fieldBmbt:
  89. xfss.BlockMapBTree, err = btreeStats(us)
  90. case fieldDir:
  91. xfss.DirectoryOperation, err = directoryOperationStats(us)
  92. case fieldTrans:
  93. xfss.Transaction, err = transactionStats(us)
  94. case fieldIg:
  95. xfss.InodeOperation, err = inodeOperationStats(us)
  96. case fieldLog:
  97. xfss.LogOperation, err = logOperationStats(us)
  98. case fieldRw:
  99. xfss.ReadWrite, err = readWriteStats(us)
  100. case fieldAttr:
  101. xfss.AttributeOperation, err = attributeOperationStats(us)
  102. case fieldIcluster:
  103. xfss.InodeClustering, err = inodeClusteringStats(us)
  104. case fieldVnodes:
  105. xfss.Vnode, err = vnodeStats(us)
  106. case fieldBuf:
  107. xfss.Buffer, err = bufferStats(us)
  108. case fieldPushAil:
  109. xfss.PushAil, err = pushAilStats(us)
  110. case fieldXstrat:
  111. xfss.Xstrat, err = xStratStats(us)
  112. case fieldAbtb2:
  113. xfss.BtreeAllocBlocks2, err = btreeAllocBlocks2Stats(us)
  114. case fieldAbtc2:
  115. xfss.BtreeAllocContig2, err = btreeAllocContig2Stats(us)
  116. case fieldBmbt2:
  117. xfss.BtreeBlockMap2, err = btreeBlockMap2Stats(us)
  118. case fieldIbt2:
  119. xfss.BtreeInode2, err = btreeInode2Stats(us)
  120. //case fieldFibt2:
  121. case fieldQm:
  122. xfss.QuotaManager, err = quotaManagerStats(us)
  123. case fieldDebug:
  124. xfss.Debug, err = debugStats(us)
  125. }
  126. if err != nil {
  127. return nil, err
  128. }
  129. }
  130. return &xfss, s.Err()
  131. }
  132. // extentAllocationStats builds an ExtentAllocationStats from a slice of uint32s.
  133. func extentAllocationStats(us []uint32) (ExtentAllocationStats, error) {
  134. if l := len(us); l != 4 {
  135. return ExtentAllocationStats{}, fmt.Errorf("incorrect number of values for XFS extent allocation stats: %d", l)
  136. }
  137. return ExtentAllocationStats{
  138. ExtentsAllocated: us[0],
  139. BlocksAllocated: us[1],
  140. ExtentsFreed: us[2],
  141. BlocksFreed: us[3],
  142. }, nil
  143. }
  144. // btreeStats builds a BTreeStats from a slice of uint32s.
  145. func btreeStats(us []uint32) (BTreeStats, error) {
  146. if l := len(us); l != 4 {
  147. return BTreeStats{}, fmt.Errorf("incorrect number of values for XFS btree stats: %d", l)
  148. }
  149. return BTreeStats{
  150. Lookups: us[0],
  151. Compares: us[1],
  152. RecordsInserted: us[2],
  153. RecordsDeleted: us[3],
  154. }, nil
  155. }
  156. // BlockMappingStat builds a BlockMappingStats from a slice of uint32s.
  157. func blockMappingStats(us []uint32) (BlockMappingStats, error) {
  158. if l := len(us); l != 7 {
  159. return BlockMappingStats{}, fmt.Errorf("incorrect number of values for XFS block mapping stats: %d", l)
  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. // DirectoryOperationStats builds a DirectoryOperationStats from a slice of uint32s.
  172. func directoryOperationStats(us []uint32) (DirectoryOperationStats, error) {
  173. if l := len(us); l != 4 {
  174. return DirectoryOperationStats{}, fmt.Errorf("incorrect number of values for XFS directory operation stats: %d", l)
  175. }
  176. return DirectoryOperationStats{
  177. Lookups: us[0],
  178. Creates: us[1],
  179. Removes: us[2],
  180. Getdents: us[3],
  181. }, nil
  182. }
  183. // TransactionStats builds a TransactionStats from a slice of uint32s.
  184. func transactionStats(us []uint32) (TransactionStats, error) {
  185. if l := len(us); l != 3 {
  186. return TransactionStats{}, fmt.Errorf("incorrect number of values for XFS transaction stats: %d", l)
  187. }
  188. return TransactionStats{
  189. Sync: us[0],
  190. Async: us[1],
  191. Empty: us[2],
  192. }, nil
  193. }
  194. // InodeOperationStats builds an InodeOperationStats from a slice of uint32s.
  195. func inodeOperationStats(us []uint32) (InodeOperationStats, error) {
  196. if l := len(us); l != 7 {
  197. return InodeOperationStats{}, fmt.Errorf("incorrect number of values for XFS inode operation stats: %d", l)
  198. }
  199. return InodeOperationStats{
  200. Attempts: us[0],
  201. Found: us[1],
  202. Recycle: us[2],
  203. Missed: us[3],
  204. Duplicate: us[4],
  205. Reclaims: us[5],
  206. AttributeChange: us[6],
  207. }, nil
  208. }
  209. // LogOperationStats builds a LogOperationStats from a slice of uint32s.
  210. func logOperationStats(us []uint32) (LogOperationStats, error) {
  211. if l := len(us); l != 5 {
  212. return LogOperationStats{}, fmt.Errorf("incorrect number of values for XFS log operation stats: %d", l)
  213. }
  214. return LogOperationStats{
  215. Writes: us[0],
  216. Blocks: us[1],
  217. NoInternalBuffers: us[2],
  218. Force: us[3],
  219. ForceSleep: us[4],
  220. }, nil
  221. }
  222. // pushAilStats handles push_ail stats.
  223. func pushAilStats(us []uint32) (PushAilStats, error) {
  224. if l := len(us); l != 10 {
  225. return PushAilStats{}, fmt.Errorf("incorrect number of values for XFS push ail stats: %d", l)
  226. }
  227. return PushAilStats{
  228. TryLogspace: us[0],
  229. SleepLogspace: us[1],
  230. Pushes: us[2],
  231. Success: us[3],
  232. PushBuf: us[4],
  233. Pinned: us[5],
  234. Locked: us[6],
  235. Flushing: us[7],
  236. Restarts: us[8],
  237. Flush: us[9],
  238. }, nil
  239. }
  240. // xStratStats handles xstrat stats.
  241. func xStratStats(us []uint32) (XstratStats, error) {
  242. if l := len(us); l != 2 {
  243. return XstratStats{}, fmt.Errorf("incorrect number of values for XFS xstrat stats: %d", l)
  244. }
  245. return XstratStats{
  246. Quick: us[0],
  247. Split: us[1],
  248. }, nil
  249. }
  250. // readWriteStats handles rw stats.
  251. func readWriteStats(us []uint32) (ReadWriteStats, error) {
  252. if l := len(us); l != 2 {
  253. return ReadWriteStats{}, fmt.Errorf("incorrect number of values for XFS read write stats: %d", l)
  254. }
  255. return ReadWriteStats{
  256. Write: us[0],
  257. Read: us[1],
  258. }, nil
  259. }
  260. // AttributeOperationStats builds an AttributeOperationStats from a slice of uint32s.
  261. func attributeOperationStats(us []uint32) (AttributeOperationStats, error) {
  262. if l := len(us); l != 4 {
  263. return AttributeOperationStats{}, fmt.Errorf("incorrect number of values for XFS attribute operation stats: %d", l)
  264. }
  265. return AttributeOperationStats{
  266. Get: us[0],
  267. Set: us[1],
  268. Remove: us[2],
  269. List: us[3],
  270. }, nil
  271. }
  272. // InodeClusteringStats builds an InodeClusteringStats from a slice of uint32s.
  273. func inodeClusteringStats(us []uint32) (InodeClusteringStats, error) {
  274. if l := len(us); l != 3 {
  275. return InodeClusteringStats{}, fmt.Errorf("incorrect number of values for XFS inode clustering stats: %d", l)
  276. }
  277. return InodeClusteringStats{
  278. Iflush: us[0],
  279. Flush: us[1],
  280. FlushInode: us[2],
  281. }, nil
  282. }
  283. // VnodeStats builds a VnodeStats from a slice of uint32s.
  284. func vnodeStats(us []uint32) (VnodeStats, error) {
  285. // The attribute "Free" appears to not be available on older XFS
  286. // stats versions. Therefore, 7 or 8 elements may appear in
  287. // this slice.
  288. l := len(us)
  289. if l != 7 && l != 8 {
  290. return VnodeStats{}, fmt.Errorf("incorrect number of values for XFS vnode stats: %d", l)
  291. }
  292. s := VnodeStats{
  293. Active: us[0],
  294. Allocate: us[1],
  295. Get: us[2],
  296. Hold: us[3],
  297. Release: us[4],
  298. Reclaim: us[5],
  299. Remove: us[6],
  300. }
  301. // Skip adding free, unless it is present. The zero value will
  302. // be used in place of an actual count.
  303. if l == 7 {
  304. return s, nil
  305. }
  306. s.Free = us[7]
  307. return s, nil
  308. }
  309. // BufferStats builds a BufferStats from a slice of uint32s.
  310. func bufferStats(us []uint32) (BufferStats, error) {
  311. if l := len(us); l != 9 {
  312. return BufferStats{}, fmt.Errorf("incorrect number of values for XFS buffer stats: %d", l)
  313. }
  314. return BufferStats{
  315. Get: us[0],
  316. Create: us[1],
  317. GetLocked: us[2],
  318. GetLockedWaited: us[3],
  319. BusyLocked: us[4],
  320. MissLocked: us[5],
  321. PageRetries: us[6],
  322. PageFound: us[7],
  323. GetRead: us[8],
  324. }, nil
  325. }
  326. // ExtendedPrecisionStats builds an ExtendedPrecisionStats from a slice of uint32s.
  327. func extendedPrecisionStats(us []uint64) (ExtendedPrecisionStats, error) {
  328. if l := len(us); l != 3 {
  329. return ExtendedPrecisionStats{}, fmt.Errorf("incorrect number of values for XFS extended precision stats: %d", l)
  330. }
  331. return ExtendedPrecisionStats{
  332. FlushBytes: us[0],
  333. WriteBytes: us[1],
  334. ReadBytes: us[2],
  335. }, nil
  336. }
  337. func quotaManagerStats(us []uint32) (QuotaManagerStats, error) {
  338. // The "Unused" attribute first appears in Linux 4.20
  339. // As a result either 8 or 9 elements may appear in this slice depending on
  340. // the kernel version.
  341. l := len(us)
  342. if l != 8 && l != 9 {
  343. return QuotaManagerStats{}, fmt.Errorf("incorrect number of values for XFS quota stats: %d", l)
  344. }
  345. s := QuotaManagerStats{
  346. Reclaims: us[0],
  347. ReclaimMisses: us[1],
  348. DquoteDups: us[2],
  349. CacheMisses: us[3],
  350. CacheHits: us[4],
  351. Wants: us[5],
  352. ShakeReclaims: us[6],
  353. InactReclaims: us[7],
  354. }
  355. if l > 8 {
  356. s.Unused = us[8]
  357. }
  358. return s, nil
  359. }
  360. func debugStats(us []uint32) (DebugStats, error) {
  361. if l := len(us); l != 1 {
  362. return DebugStats{}, fmt.Errorf("incorrect number of values for XFS debug stats: %d", l)
  363. }
  364. return DebugStats{
  365. Enabled: us[0],
  366. }, nil
  367. }
  368. // btreeAllocBlocks2Stats handles abtb2 stats.
  369. func btreeAllocBlocks2Stats(us []uint32) (BtreeAllocBlocks2Stats, error) {
  370. if l := len(us); l != 15 {
  371. return BtreeAllocBlocks2Stats{}, fmt.Errorf("incorrect number of values for abtb2 stats: %d", 1)
  372. }
  373. return BtreeAllocBlocks2Stats{
  374. Lookup: us[0],
  375. Compare: us[1],
  376. Insrec: us[2],
  377. Delrec: us[3],
  378. NewRoot: us[4],
  379. KillRoot: us[5],
  380. Increment: us[6],
  381. Decrement: us[7],
  382. Lshift: us[8],
  383. Rshift: us[9],
  384. Split: us[10],
  385. Join: us[11],
  386. Alloc: us[12],
  387. Free: us[13],
  388. Moves: us[14],
  389. }, nil
  390. }
  391. // btreeAllocContig2Stats handles abtc2 stats.
  392. func btreeAllocContig2Stats(us []uint32) (BtreeAllocContig2Stats, error) {
  393. if l := len(us); l != 15 {
  394. return BtreeAllocContig2Stats{}, fmt.Errorf("incorrect number of values for abtc2 stats: %d", 1)
  395. }
  396. return BtreeAllocContig2Stats{
  397. Lookup: us[0],
  398. Compare: us[1],
  399. Insrec: us[2],
  400. Delrec: us[3],
  401. NewRoot: us[4],
  402. KillRoot: us[5],
  403. Increment: us[6],
  404. Decrement: us[7],
  405. Lshift: us[8],
  406. Rshift: us[9],
  407. Split: us[10],
  408. Join: us[11],
  409. Alloc: us[12],
  410. Free: us[13],
  411. Moves: us[14],
  412. }, nil
  413. }
  414. // btreeBlockMap2Stats handles bmbt2 stats.
  415. func btreeBlockMap2Stats(us []uint32) (BtreeBlockMap2Stats, error) {
  416. if l := len(us); l != 15 {
  417. return BtreeBlockMap2Stats{}, fmt.Errorf("incorrect number of values for bmbt2 stats: %d", 1)
  418. }
  419. return BtreeBlockMap2Stats{
  420. Lookup: us[0],
  421. Compare: us[1],
  422. Insrec: us[2],
  423. Delrec: us[3],
  424. NewRoot: us[4],
  425. KillRoot: us[5],
  426. Increment: us[6],
  427. Decrement: us[7],
  428. Lshift: us[8],
  429. Rshift: us[9],
  430. Split: us[10],
  431. Join: us[11],
  432. Alloc: us[12],
  433. Free: us[13],
  434. Moves: us[14],
  435. }, nil
  436. }
  437. // btreeInode2Stats handles ibt2 stats.
  438. func btreeInode2Stats(us []uint32) (BtreeInode2Stats, error) {
  439. if l := len(us); l != 15 {
  440. return BtreeInode2Stats{}, fmt.Errorf("incorrect number of values for ibt2 stats: %d", 1)
  441. }
  442. return BtreeInode2Stats{
  443. Lookup: us[0],
  444. Compare: us[1],
  445. Insrec: us[2],
  446. Delrec: us[3],
  447. NewRoot: us[4],
  448. KillRoot: us[5],
  449. Increment: us[6],
  450. Decrement: us[7],
  451. Lshift: us[8],
  452. Rshift: us[9],
  453. Split: us[10],
  454. Join: us[11],
  455. Alloc: us[12],
  456. Free: us[13],
  457. Moves: us[14],
  458. }, nil
  459. }