Skip to content

Commit 4bc37fa

Browse files
lifubangkolyshkin
authored andcommitted
fs: improve cpuacct.usage_all parsing
First, we can reasonably expect the first three fields of cpuacct.usage_all to be "cpu user system", even for future kernels, and if we see something different, it doesn't make sense to continue. So check that the header is as expected, and error out otherwise. Second, if we have more than 3 values, and we've checked that the first 3 are as expected ("cpu user system"), we can safely ignore extra columns. Add appropriate tests. Fixes issue 46. Reported-by: vimiix <[email protected]> Signed-off-by: Kir Kolyshkin <[email protected]>
1 parent c63eee3 commit 4bc37fa

File tree

2 files changed

+52
-4
lines changed

2 files changed

+52
-4
lines changed

fs/cpuacct.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,16 @@ func getPercpuUsageInModes(path string) ([]uint64, []uint64, error) {
129129
defer fd.Close()
130130

131131
scanner := bufio.NewScanner(fd)
132-
scanner.Scan() // skipping header line
132+
scanner.Scan() // Read header line.
133+
const want = "cpu user system"
134+
if hdr := scanner.Text(); !strings.HasPrefix(hdr, want) {
135+
return nil, nil, malformedLine(path, file, hdr)
136+
}
133137

134138
for scanner.Scan() {
135-
// Each line is: cpu user system
136-
fields := strings.SplitN(scanner.Text(), " ", 3)
137-
if len(fields) != 3 {
139+
// Each line is: cpu user system. Keep N at 4 to ignore extra fields.
140+
fields := strings.SplitN(scanner.Text(), " ", 4)
141+
if len(fields) < 3 {
138142
continue
139143
}
140144

fs/cpuacct_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package fs
22

33
import (
44
"reflect"
5+
"slices"
56
"testing"
67

78
"github.com/opencontainers/cgroups"
@@ -96,6 +97,49 @@ func TestCpuacctStatsWithoutUsageAll(t *testing.T) {
9697
}
9798
}
9899

100+
func TestCpuacctUsageAllExtra(t *testing.T) {
101+
path := tempDir(t, "cpuacct")
102+
err := cgroups.WriteFile(path, "cpuacct.usage_all",
103+
`cpu user system bt_user bt_system
104+
0 962250696038415 637727786389114 0 0
105+
1 981956408513304 638197595421064 0 0
106+
`)
107+
if err != nil {
108+
t.Fatal(err)
109+
}
110+
111+
system, user, err := getPercpuUsageInModes(path)
112+
if err != nil {
113+
t.Fatal(err)
114+
}
115+
116+
expUser := []uint64{962250696038415, 981956408513304}
117+
expSystem := []uint64{637727786389114, 638197595421064}
118+
if !slices.Equal(user, expUser) {
119+
t.Fatalf("unexpected user data (want %+v, got %+v", expUser, user)
120+
}
121+
if !slices.Equal(system, expSystem) {
122+
t.Fatalf("unexpected system data (want %+v, got +%v)", expSystem, system)
123+
}
124+
}
125+
126+
func TestCpuacctUsageAllBad(t *testing.T) {
127+
path := tempDir(t, "cpuacct")
128+
err := cgroups.WriteFile(path, "cpuacct.usage_all",
129+
`cpu bad data fields
130+
0 1 2
131+
`)
132+
if err != nil {
133+
t.Fatal(err)
134+
}
135+
136+
_, _, err = getPercpuUsageInModes(path)
137+
t.Log(err)
138+
if err == nil {
139+
t.Fatal("want error, got nil")
140+
}
141+
}
142+
99143
func BenchmarkGetCpuUsageBreakdown(b *testing.B) {
100144
path := tempDir(b, "cpuacct")
101145
writeFileContents(b, path, map[string]string{

0 commit comments

Comments
 (0)