// Copyright 2020 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" "bytes" "fmt" "regexp" "strconv" "strings" "github.com/prometheus/procfs/internal/util" ) var ( slabSpace = regexp.MustCompile(`\s+`) slabVer = regexp.MustCompile(`slabinfo -`) slabHeader = regexp.MustCompile(`# name`) ) // Slab represents a slab pool in the kernel. type Slab struct { Name string ObjActive int64 ObjNum int64 ObjSize int64 ObjPerSlab int64 PagesPerSlab int64 // tunables Limit int64 Batch int64 SharedFactor int64 SlabActive int64 SlabNum int64 SharedAvail int64 } // SlabInfo represents info for all slabs. type SlabInfo struct { Slabs []*Slab } func shouldParseSlab(line string) bool { if slabVer.MatchString(line) { return false } if slabHeader.MatchString(line) { return false } return true } // parseV21SlabEntry is used to parse a line from /proc/slabinfo version 2.1. func parseV21SlabEntry(line string) (*Slab, error) { // First cleanup whitespace. l := slabSpace.ReplaceAllString(line, " ") s := strings.Split(l, " ") if len(s) != 16 { return nil, fmt.Errorf("unable to parse: %q", line) } var err error i := &Slab{Name: s[0]} i.ObjActive, err = strconv.ParseInt(s[1], 10, 64) if err != nil { return nil, err } i.ObjNum, err = strconv.ParseInt(s[2], 10, 64) if err != nil { return nil, err } i.ObjSize, err = strconv.ParseInt(s[3], 10, 64) if err != nil { return nil, err } i.ObjPerSlab, err = strconv.ParseInt(s[4], 10, 64) if err != nil { return nil, err } i.PagesPerSlab, err = strconv.ParseInt(s[5], 10, 64) if err != nil { return nil, err } i.Limit, err = strconv.ParseInt(s[8], 10, 64) if err != nil { return nil, err } i.Batch, err = strconv.ParseInt(s[9], 10, 64) if err != nil { return nil, err } i.SharedFactor, err = strconv.ParseInt(s[10], 10, 64) if err != nil { return nil, err } i.SlabActive, err = strconv.ParseInt(s[13], 10, 64) if err != nil { return nil, err } i.SlabNum, err = strconv.ParseInt(s[14], 10, 64) if err != nil { return nil, err } i.SharedAvail, err = strconv.ParseInt(s[15], 10, 64) if err != nil { return nil, err } return i, nil } // parseSlabInfo21 is used to parse a slabinfo 2.1 file. func parseSlabInfo21(r *bytes.Reader) (SlabInfo, error) { scanner := bufio.NewScanner(r) s := SlabInfo{Slabs: []*Slab{}} for scanner.Scan() { line := scanner.Text() if !shouldParseSlab(line) { continue } slab, err := parseV21SlabEntry(line) if err != nil { return s, err } s.Slabs = append(s.Slabs, slab) } return s, nil } // SlabInfo reads data from `/proc/slabinfo`. func (fs FS) SlabInfo() (SlabInfo, error) { // TODO: Consider passing options to allow for parsing different // slabinfo versions. However, slabinfo 2.1 has been stable since // kernel 2.6.10 and later. data, err := util.ReadFileNoStat(fs.proc.Path("slabinfo")) if err != nil { return SlabInfo{}, err } return parseSlabInfo21(bytes.NewReader(data)) }