mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
Merge pull request #49734 from Shaggy84675/49709-fix_system_cpu_usage_stat
Fix docker stats parsing with large amount of interrupts
This commit is contained in:
@@ -311,6 +311,8 @@ const (
|
||||
nanoSecondsPerSecond = 1e9
|
||||
)
|
||||
|
||||
var procStatPath = "/proc/stat"
|
||||
|
||||
// getSystemCPUUsage returns the host system's cpu usage in
|
||||
// nanoseconds and number of online CPUs. An error is returned
|
||||
// if the format of the underlying file does not match.
|
||||
@@ -320,17 +322,28 @@ const (
|
||||
// provided. See `man 5 proc` for details on specific field
|
||||
// information.
|
||||
func getSystemCPUUsage() (cpuUsage uint64, cpuNum uint32, _ error) {
|
||||
f, err := os.Open("/proc/stat")
|
||||
f, err := os.Open(procStatPath)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if len(line) < 4 || line[:3] != "cpu" {
|
||||
break // Assume all cpu* records are at the front, like glibc https://github.com/bminor/glibc/blob/5d00c201b9a2da768a79ea8d5311f257871c0b43/sysdeps/unix/sysv/linux/getsysstats.c#L108-L135
|
||||
rdr := bufio.NewReaderSize(f, 1024)
|
||||
|
||||
for {
|
||||
data, isPartial, err := rdr.ReadLine()
|
||||
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("error scanning '%s' file: %w", procStatPath, err)
|
||||
}
|
||||
// Assume all cpu* records are at the start of the file, like glibc:
|
||||
// https://github.com/bminor/glibc/blob/5d00c201b9a2da768a79ea8d5311f257871c0b43/sysdeps/unix/sysv/linux/getsysstats.c#L108-L135
|
||||
if isPartial || len(data) < 4 {
|
||||
break
|
||||
}
|
||||
line := string(data)
|
||||
if line[:3] != "cpu" {
|
||||
break
|
||||
}
|
||||
if line[3] == ' ' {
|
||||
parts := strings.Fields(line)
|
||||
@@ -352,9 +365,5 @@ func getSystemCPUUsage() (cpuUsage uint64, cpuNum uint32, _ error) {
|
||||
cpuNum++
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return 0, 0, fmt.Errorf("error scanning '/proc/stat' file: %w", err)
|
||||
}
|
||||
return cpuUsage, cpuNum, nil
|
||||
}
|
||||
|
||||
30
daemon/stats_unix_test.go
Normal file
30
daemon/stats_unix_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
//go:build !windows
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestGetSystemCPUUsageParsing(t *testing.T) {
|
||||
dummyFilePath := filepath.Join("testdata", "stat")
|
||||
expectedCpuUsage := uint64(65647090000000)
|
||||
expectedCpuNum := uint32(128)
|
||||
|
||||
origStatPath := procStatPath
|
||||
procStatPath = dummyFilePath
|
||||
defer func() { procStatPath = origStatPath }()
|
||||
|
||||
_, err := os.Stat(dummyFilePath)
|
||||
assert.NilError(t, err)
|
||||
|
||||
cpuUsage, cpuNum, err := getSystemCPUUsage()
|
||||
|
||||
assert.Equal(t, cpuUsage, expectedCpuUsage)
|
||||
assert.Equal(t, cpuNum, expectedCpuNum)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
136
daemon/testdata/stat
vendored
Normal file
136
daemon/testdata/stat
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user