Skip to content

Commit

Permalink
Add CgroupSummary to parse /proc/cgroups (#424)
Browse files Browse the repository at this point in the history
* add CgroupSummary to parse /proc/cgroups

Signed-off-by: bielu <bellusa@qq.com>

* update copyright

Signed-off-by: bielu <bellusa@qq.com>
  • Loading branch information
biello committed Dec 9, 2021
1 parent 116b5c4 commit 0f8a320
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
98 changes: 98 additions & 0 deletions proc_cgroups.go
@@ -0,0 +1,98 @@
// Copyright 2021 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"
"strconv"
"strings"

"github.com/prometheus/procfs/internal/util"
)

// CgroupSummary models one line from /proc/cgroups.
// This file contains information about the controllers that are compiled into the kernel.
//
// Also see http://man7.org/linux/man-pages/man7/cgroups.7.html
type CgroupSummary struct {
// The name of the controller. controller is also known as subsystem.
SubsysName string
// The unique ID of the cgroup hierarchy on which this controller is mounted.
Hierarchy int
// The number of control groups in this hierarchy using this controller.
Cgroups int
// This field contains the value 1 if this controller is enabled, or 0 if it has been disabled
Enabled int
}

// parseCgroupSummary parses each line of the /proc/cgroup file
// Line format is `subsys_name hierarchy num_cgroups enabled`.
func parseCgroupSummaryString(CgroupSummaryStr string) (*CgroupSummary, error) {
var err error

fields := strings.Fields(CgroupSummaryStr)
// require at least 4 fields
if len(fields) < 4 {
return nil, fmt.Errorf("at least 4 fields required, found %d fields in cgroup info string: %s", len(fields), CgroupSummaryStr)
}

CgroupSummary := &CgroupSummary{
SubsysName: fields[0],
}
CgroupSummary.Hierarchy, err = strconv.Atoi(fields[1])
if err != nil {
return nil, fmt.Errorf("failed to parse hierarchy ID")
}
CgroupSummary.Cgroups, err = strconv.Atoi(fields[2])
if err != nil {
return nil, fmt.Errorf("failed to parse Cgroup Num")
}
CgroupSummary.Enabled, err = strconv.Atoi(fields[3])
if err != nil {
return nil, fmt.Errorf("failed to parse Enabled")
}
return CgroupSummary, nil
}

// parseCgroupSummary reads each line of the /proc/cgroup file.
func parseCgroupSummary(data []byte) ([]CgroupSummary, error) {
var CgroupSummarys []CgroupSummary
scanner := bufio.NewScanner(bytes.NewReader(data))
for scanner.Scan() {
CgroupSummaryString := scanner.Text()
// ignore comment lines
if strings.HasPrefix(CgroupSummaryString, "#") {
continue
}
CgroupSummary, err := parseCgroupSummaryString(CgroupSummaryString)
if err != nil {
return nil, err
}
CgroupSummarys = append(CgroupSummarys, *CgroupSummary)
}

err := scanner.Err()
return CgroupSummarys, err
}

// CgroupSummarys returns information about current /proc/cgroups.
func (fs FS) CgroupSummarys() ([]CgroupSummary, error) {
data, err := util.ReadFileNoStat(fs.proc.Path("cgroups"))
if err != nil {
return nil, err
}
return parseCgroupSummary(data)
}
64 changes: 64 additions & 0 deletions proc_cgroups_test.go
@@ -0,0 +1,64 @@
// Copyright 2021 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 (
"reflect"
"testing"
)

func TestParseCgroupSummaryString(t *testing.T) {
tests := []struct {
name string
s string
shouldErr bool
CgroupSummary *CgroupSummary
}{
{
name: "cpuset simple line",
s: "cpuset 7 148 1",
shouldErr: false,
CgroupSummary: &CgroupSummary{
SubsysName: "cpuset",
Hierarchy: 7,
Cgroups: 148,
Enabled: 1,
},
},
{
name: "memory cgroup number mis format",
s: "memory 9 ## 1",
shouldErr: true,
CgroupSummary: nil,
},
}

for i, test := range tests {
t.Logf("[%02d] test %q", i, test.name)

CgroupSummary, err := parseCgroupSummaryString(test.s)

if test.shouldErr && err == nil {
t.Errorf("%s: expected an error, but none occurred", test.name)
}
if !test.shouldErr && err != nil {
t.Errorf("%s: unexpected error: %v", test.name, err)
}

if want, have := test.CgroupSummary, CgroupSummary; !reflect.DeepEqual(want, have) {
t.Errorf("cgroup:\nwant:\n%+v\nhave:\n%+v", want, have)
}
}

}

0 comments on commit 0f8a320

Please sign in to comment.