parse.go 9.5 KB

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