Skip to content

Commit

Permalink
Merge pull request #3396 from brancz/ulimit-parse-perf
Browse files Browse the repository at this point in the history
container/libcontainer: Improve limits file parsing perf
  • Loading branch information
bobbypage authored Sep 26, 2023
2 parents fbd519b + 4c90b90 commit 28d5bdd
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 35 deletions.
71 changes: 36 additions & 35 deletions container/libcontainer/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import (
)

var (
whitelistedUlimits = [...]string{"max_open_files"}
referencedResetInterval = flag.Uint64("referenced_reset_interval", 0,
"Reset interval for referenced bytes (container_referenced_bytes metric), number of measurement cycles after which referenced bytes are cleared, if set to 0 referenced bytes are never cleared (default: 0)")

Expand Down Expand Up @@ -205,51 +204,53 @@ func parseUlimit(value string) (int64, error) {
return num, nil
}

func isUlimitWhitelisted(name string) bool {
for _, whitelist := range whitelistedUlimits {
if name == whitelist {
return true
}
}
return false
}

func processLimitsFile(fileData string) []info.UlimitSpec {
const maxOpenFilesLinePrefix = "Max open files"

limits := strings.Split(fileData, "\n")
ulimits := make([]info.UlimitSpec, 0, len(limits))
for _, lim := range limits {
// Skip any headers/footers
if strings.HasPrefix(lim, "Max") {

// Line format: Max open files 16384 16384 files
fields := regexp.MustCompile(`[\s]{2,}`).Split(lim, -1)
name := strings.Replace(strings.ToLower(strings.TrimSpace(fields[0])), " ", "_", -1)

found := isUlimitWhitelisted(name)
if !found {
continue
}

soft := strings.TrimSpace(fields[1])
softNum, softErr := parseUlimit(soft)

hard := strings.TrimSpace(fields[2])
hardNum, hardErr := parseUlimit(hard)

// Omit metric if there were any parsing errors
if softErr == nil && hardErr == nil {
ulimitSpec := info.UlimitSpec{
Name: name,
SoftLimit: int64(softNum),
HardLimit: int64(hardNum),
}
ulimits = append(ulimits, ulimitSpec)
if strings.HasPrefix(lim, "Max open files") {
// Remove line prefix
ulimit, err := processMaxOpenFileLimitLine(
"max_open_files",
lim[len(maxOpenFilesLinePrefix):],
)
if err == nil {
ulimits = append(ulimits, ulimit)
}
}
}
return ulimits
}

// Any caller of processMaxOpenFileLimitLine must ensure that the name prefix is already removed from the limit line.
// with the "Max open files" prefix.
func processMaxOpenFileLimitLine(name, line string) (info.UlimitSpec, error) {
// Remove any leading whitespace
line = strings.TrimSpace(line)
// Split on whitespace
fields := strings.Fields(line)
if len(fields) != 3 {
return info.UlimitSpec{}, fmt.Errorf("unable to parse max open files line: %s", line)
}
// The first field is the soft limit, the second is the hard limit
soft, err := parseUlimit(fields[0])
if err != nil {
return info.UlimitSpec{}, err
}
hard, err := parseUlimit(fields[1])
if err != nil {
return info.UlimitSpec{}, err
}
return info.UlimitSpec{
Name: name,
SoftLimit: soft,
HardLimit: hard,
}, nil
}

func processRootProcUlimits(rootFs string, rootPid int) []info.UlimitSpec {
filePath := path.Join(rootFs, "/proc", strconv.Itoa(rootPid), "limits")
out, err := os.ReadFile(filePath)
Expand Down
25 changes: 25 additions & 0 deletions container/libcontainer/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,3 +296,28 @@ func TestClearReferencedBytesWhenClearRefsMissing(t *testing.T) {
err := clearReferencedBytes(pids, 0, 1)
assert.Nil(t, err)
}

var ulimits []info.UlimitSpec

func BenchmarkProcessLimitsFile(b *testing.B) {
content, err := os.ReadFile("testdata/limits")
assert.Nil(b, err)

b.ResetTimer()
for i := 0; i < b.N; i++ {
ulimits = processLimitsFile(string(content))
}

// Ensure the compiler doesn't optimize away the benchmark
_ = ulimits
}

func TestProcessMaxOpenFileLimitLine(t *testing.T) {
line := " 1073741816 1073741816 files "

ulimit, err := processMaxOpenFileLimitLine("max_open_files", line)
assert.Nil(t, err)
assert.Equal(t, "max_open_files", ulimit.Name)
assert.Equal(t, int64(1073741816), ulimit.SoftLimit)
assert.Equal(t, int64(1073741816), ulimit.HardLimit)
}
17 changes: 17 additions & 0 deletions container/libcontainer/testdata/limits
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size unlimited unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 119958 119958 processes
Max open files 1073741816 1073741816 files
Max locked memory 3932852224 3932852224 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 119958 119958 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us

0 comments on commit 28d5bdd

Please sign in to comment.