1// Copyright 2020 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 procfs
15
16import (
17	"bufio"
18	"bytes"
19	"fmt"
20	"regexp"
21	"strconv"
22	"strings"
23
24	"github.com/prometheus/procfs/internal/util"
25)
26
27var (
28	slabSpace  = regexp.MustCompile(`\s+`)
29	slabVer    = regexp.MustCompile(`slabinfo -`)
30	slabHeader = regexp.MustCompile(`# name`)
31)
32
33// Slab represents a slab pool in the kernel.
34type Slab struct {
35	Name         string
36	ObjActive    int64
37	ObjNum       int64
38	ObjSize      int64
39	ObjPerSlab   int64
40	PagesPerSlab int64
41	// tunables
42	Limit        int64
43	Batch        int64
44	SharedFactor int64
45	SlabActive   int64
46	SlabNum      int64
47	SharedAvail  int64
48}
49
50// SlabInfo represents info for all slabs.
51type SlabInfo struct {
52	Slabs []*Slab
53}
54
55func shouldParseSlab(line string) bool {
56	if slabVer.MatchString(line) {
57		return false
58	}
59	if slabHeader.MatchString(line) {
60		return false
61	}
62	return true
63}
64
65// parseV21SlabEntry is used to parse a line from /proc/slabinfo version 2.1.
66func parseV21SlabEntry(line string) (*Slab, error) {
67	// First cleanup whitespace.
68	l := slabSpace.ReplaceAllString(line, " ")
69	s := strings.Split(l, " ")
70	if len(s) != 16 {
71		return nil, fmt.Errorf("unable to parse: %q", line)
72	}
73	var err error
74	i := &Slab{Name: s[0]}
75	i.ObjActive, err = strconv.ParseInt(s[1], 10, 64)
76	if err != nil {
77		return nil, err
78	}
79	i.ObjNum, err = strconv.ParseInt(s[2], 10, 64)
80	if err != nil {
81		return nil, err
82	}
83	i.ObjSize, err = strconv.ParseInt(s[3], 10, 64)
84	if err != nil {
85		return nil, err
86	}
87	i.ObjPerSlab, err = strconv.ParseInt(s[4], 10, 64)
88	if err != nil {
89		return nil, err
90	}
91	i.PagesPerSlab, err = strconv.ParseInt(s[5], 10, 64)
92	if err != nil {
93		return nil, err
94	}
95	i.Limit, err = strconv.ParseInt(s[8], 10, 64)
96	if err != nil {
97		return nil, err
98	}
99	i.Batch, err = strconv.ParseInt(s[9], 10, 64)
100	if err != nil {
101		return nil, err
102	}
103	i.SharedFactor, err = strconv.ParseInt(s[10], 10, 64)
104	if err != nil {
105		return nil, err
106	}
107	i.SlabActive, err = strconv.ParseInt(s[13], 10, 64)
108	if err != nil {
109		return nil, err
110	}
111	i.SlabNum, err = strconv.ParseInt(s[14], 10, 64)
112	if err != nil {
113		return nil, err
114	}
115	i.SharedAvail, err = strconv.ParseInt(s[15], 10, 64)
116	if err != nil {
117		return nil, err
118	}
119	return i, nil
120}
121
122// parseSlabInfo21 is used to parse a slabinfo 2.1 file.
123func parseSlabInfo21(r *bytes.Reader) (SlabInfo, error) {
124	scanner := bufio.NewScanner(r)
125	s := SlabInfo{Slabs: []*Slab{}}
126	for scanner.Scan() {
127		line := scanner.Text()
128		if !shouldParseSlab(line) {
129			continue
130		}
131		slab, err := parseV21SlabEntry(line)
132		if err != nil {
133			return s, err
134		}
135		s.Slabs = append(s.Slabs, slab)
136	}
137	return s, nil
138}
139
140// SlabInfo reads data from /proc/slabinfo
141func (fs FS) SlabInfo() (SlabInfo, error) {
142	// TODO: Consider passing options to allow for parsing different
143	// slabinfo versions. However, slabinfo 2.1 has been stable since
144	// kernel 2.6.10 and later.
145	data, err := util.ReadFileNoStat(fs.proc.Path("slabinfo"))
146	if err != nil {
147		return SlabInfo{}, err
148	}
149
150	return parseSlabInfo21(bytes.NewReader(data))
151}
152